From c1bf9f4fb1191cdbe8f1019031602ec846ba1e59 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 26 Oct 2025 09:55:49 +0000 Subject: [PATCH 1/4] [CS] Invalidate auxiliary vars in `markInvalid` And make sure we mark any PatternBindingDecl entries as having been checked to avoid re-checking. This fixes a crash where we could attempt to re-check a property wrapper to compute its backing type. --- lib/Sema/SyntacticElementTarget.cpp | 27 +++++++++++++++---- ...nstraintSystem-getClosureType-4a4392.swift | 11 ++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-4a4392.swift diff --git a/lib/Sema/SyntacticElementTarget.cpp b/lib/Sema/SyntacticElementTarget.cpp index 6aa7a9e33cdcd..a23a7e281e343 100644 --- a/lib/Sema/SyntacticElementTarget.cpp +++ b/lib/Sema/SyntacticElementTarget.cpp @@ -306,14 +306,31 @@ void SyntacticElementTarget::markInvalid() const { return Action::Continue(P); } + void invalidateVarDecl(VarDecl *VD) { + // Only set invalid if we don't already have an interface type computed. + if (!VD->hasInterfaceType()) + VD->setInvalid(); + + // Also do the same for any auxiliary vars. + VD->visitAuxiliaryVars(/*forNameLookup*/ false, [&](VarDecl *VD) { + invalidateVarDecl(VD); + }); + } + PreWalkAction walkToDeclPre(Decl *D) override { // Mark any VarDecls and PatternBindingDecls as invalid. if (auto *VD = dyn_cast(D)) { - // Only set invalid if we don't already have an interface type computed. - if (!VD->hasInterfaceType()) - D->setInvalid(); - } else if (isa(D)) { - D->setInvalid(); + invalidateVarDecl(VD); + } else if (auto *PBD = dyn_cast(D)) { + PBD->setInvalid(); + // Make sure we mark the patterns and initializers as having been + // checked, otherwise `typeCheckPatternBinding` might try to check them + // again. + for (auto i : range(0, PBD->getNumPatternEntries())) { + PBD->setPattern(i, PBD->getPattern(i), /*fullyValidated*/ true); + if (PBD->isInitialized(i)) + PBD->setInitializerChecked(i); + } } return Action::VisitNodeIf(isa(D)); } diff --git a/validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-4a4392.swift b/validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-4a4392.swift new file mode 100644 index 0000000000000..540ad11ea6085 --- /dev/null +++ b/validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-4a4392.swift @@ -0,0 +1,11 @@ +// {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::getClosureType(swift::ClosureExpr const*) const","signatureAssert":"Assertion failed: (result), function getClosureType"} +// RUN: not %target-swift-frontend -typecheck %s +@propertyWrapper struct a { +} +{ + @a var x = + if <#expression#> { + return + } + _x +} From 346acda3e5f6fca51f9d4315294a243b45d6076e Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 26 Oct 2025 09:55:49 +0000 Subject: [PATCH 2/4] [CS] Set property wrapper auxiliary var types in constraint generation Rather than computing these each time we need to solve an element that has a reference to them, let's just set them up-front when we generate constraints for the corresponding variable. --- lib/Sema/CSSyntacticElement.cpp | 53 ++++++++++++++------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 9fc7fb6a92e5b..bc2b064ca07ee 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -141,36 +141,6 @@ class TypeVariableRefFinder : public ASTWalker { if (!var) return Action::Continue(expr); - if (auto *wrappedVar = var->getOriginalWrappedProperty()) { - // If there is no type it means that the body of the - // closure hasn't been resolved yet, so we can - // just skip it and wait for \c applyPropertyWrapperToParameter - // to assign types. - if (wrappedVar->hasImplicitPropertyWrapper()) - return Action::Continue(expr); - - auto outermostWrapperAttr = - wrappedVar->getOutermostAttachedPropertyWrapper(); - - // If the attribute doesn't have a type it could only mean - // that the declaration was incorrect. - if (!CS.hasType(outermostWrapperAttr->getTypeExpr())) - return Action::Continue(expr); - - auto wrapperType = - CS.simplifyType(CS.getType(outermostWrapperAttr->getTypeExpr())); - - if (var->getName().hasDollarPrefix()) { - // $ is the projected value var - CS.setType(var, computeProjectedValueType(wrappedVar, wrapperType)); - } else { - // _ is the wrapper var - CS.setType(var, wrapperType); - } - - return Action::Continue(expr); - } - // If there is no type recorded yet, let's check whether // it is a placeholder variable implicitly generated by the // compiler. @@ -817,6 +787,26 @@ class SyntacticElementConstraintGenerator patternType); } + void setPropertyWrapperAuxiliaryTypes(const SyntacticElementTarget &target) { + if (!target.isForInitialization()) + return; + + auto *wrappedVar = target.getInitializationWrappedVar(); + if (!wrappedVar) + return; + + auto *outermostAttr = wrappedVar->getOutermostAttachedPropertyWrapper(); + auto *wrapperTypeExpr = outermostAttr->getTypeExpr(); + + auto wrapperTy = cs.simplifyType(cs.getType(wrapperTypeExpr)); + if (auto *projectedVal = wrappedVar->getPropertyWrapperProjectionVar()) { + auto projectedTy = computeProjectedValueType(wrappedVar, wrapperTy); + cs.setType(projectedVal, projectedTy); + } + if (auto *backing = wrappedVar->getPropertyWrapperBackingProperty()) + cs.setType(backing, wrapperTy); + } + void visitPatternBindingElement(PatternBindingDecl *patternBinding) { assert(locator->isLastElement()); @@ -847,6 +837,9 @@ class SyntacticElementConstraintGenerator hadError = true; return; } + + // Set the types of any auxiliary property wrapper vars. + setPropertyWrapperAuxiliaryTypes(*target); } void visitDecl(Decl *decl) { From 3b57a7cd91186608076cc2a3874ab0258f185250 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 26 Oct 2025 09:55:49 +0000 Subject: [PATCH 3/4] [CS] Clean up some property wrapper logic These methods can be simplified a bunch since the returned decl is always the input decl and we can refactor the lambdas to just return the auxiliary variable and have the type computation in the caller. --- include/swift/Sema/ConstraintSystem.h | 24 +++---- lib/Sema/CSSimplify.cpp | 35 +++++------ lib/Sema/ConstraintSystem.cpp | 91 +++++++++++---------------- test/decl/var/property_wrappers.swift | 9 +++ 4 files changed, 71 insertions(+), 88 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index df9701e508aa0..29ef2ac059686 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -3973,21 +3973,17 @@ class ConstraintSystem { isRepresentativeFor(TypeVariableType *typeVar, ConstraintLocator::PathElementKind kind) const; - /// Gets the VarDecl associated with resolvedOverload, and the type of the - /// projection if the decl has an associated property wrapper with a projectedValue. - std::optional> - getPropertyWrapperProjectionInfo(SelectedOverload resolvedOverload); - - /// Gets the VarDecl associated with resolvedOverload, and the type of the - /// backing storage if the decl has an associated property wrapper. - std::optional> - getPropertyWrapperInformation(SelectedOverload resolvedOverload); - - /// Gets the VarDecl, and the type of the type property that it wraps if - /// resolved overload has a decl which is the backing storage for a + /// Gets the the type of the projection if the decl has an associated property + /// wrapper with a projectedValue. + Type getPropertyWrapperProjectionType(SelectedOverload resolvedOverload); + + /// Gets the type of the backing storage if the decl has an associated /// property wrapper. - std::optional> - getWrappedPropertyInformation(SelectedOverload resolvedOverload); + Type getPropertyWrapperBackingType(SelectedOverload resolvedOverload); + + /// Gets the type of a wrapped property if resolved overload has + /// a decl which is the backing storage for a property wrapper. + Type getWrappedPropertyType(SelectedOverload resolvedOverload); /// Merge the equivalence sets of the two type variables. /// diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 6e4803937daa7..fa0c5405d83d6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4825,14 +4825,19 @@ static ConstraintFix *fixPropertyWrapperFailure( if (!resolvedOverload) return nullptr; + auto *decl = resolvedOverload->choice.getDeclOrNull(); + auto *VD = dyn_cast_or_null(decl); + if (!VD) + return nullptr; + enum class Fix : uint8_t { ProjectedValue, PropertyWrapper, WrappedValue, }; - auto applyFix = [&](Fix fix, VarDecl *decl, Type type) -> ConstraintFix * { - if (!decl->hasInterfaceType() || !type) + auto applyFix = [&](Fix fix, Type type) -> ConstraintFix * { + if (!VD->hasInterfaceType() || !type) return nullptr; if (baseTy->isEqual(type)) @@ -4841,40 +4846,34 @@ static ConstraintFix *fixPropertyWrapperFailure( if (baseTy->is() || type->is()) return nullptr; - if (!attemptFix(*resolvedOverload, decl, type)) + if (!attemptFix(*resolvedOverload, VD, type)) return nullptr; switch (fix) { case Fix::ProjectedValue: case Fix::PropertyWrapper: - return UsePropertyWrapper::create(cs, decl, fix == Fix::ProjectedValue, - baseTy, toType.value_or(type), - locator); + return UsePropertyWrapper::create(cs, VD, fix == Fix::ProjectedValue, + baseTy, toType.value_or(type), locator); case Fix::WrappedValue: - return UseWrappedValue::create(cs, decl, baseTy, toType.value_or(type), + return UseWrappedValue::create(cs, VD, baseTy, toType.value_or(type), locator); } llvm_unreachable("Unhandled Fix type in switch"); }; - if (auto projection = - cs.getPropertyWrapperProjectionInfo(*resolvedOverload)) { - if (auto *fix = applyFix(Fix::ProjectedValue, projection->first, - projection->second)) + if (auto projectTy = cs.getPropertyWrapperProjectionType(*resolvedOverload)) { + if (auto *fix = applyFix(Fix::ProjectedValue, projectTy)) return fix; } - if (auto wrapper = cs.getPropertyWrapperInformation(*resolvedOverload)) { - if (auto *fix = - applyFix(Fix::PropertyWrapper, wrapper->first, wrapper->second)) + if (auto backingTy = cs.getPropertyWrapperBackingType(*resolvedOverload)) { + if (auto *fix = applyFix(Fix::PropertyWrapper, backingTy)) return fix; } - if (auto wrappedProperty = - cs.getWrappedPropertyInformation(*resolvedOverload)) { - if (auto *fix = applyFix(Fix::WrappedValue, wrappedProperty->first, - wrappedProperty->second)) + if (auto wrappedTy = cs.getWrappedPropertyType(*resolvedOverload)) { + if (auto *fix = applyFix(Fix::WrappedValue, wrappedTy)) return fix; } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 7823676ee3e3b..e8c9bb0e08ba5 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1244,72 +1244,51 @@ TypeVariableType *ConstraintSystem::isRepresentativeFor( return *member; } -static std::optional> -getPropertyWrapperInformationFromOverload( - SelectedOverload resolvedOverload, DeclContext *DC, - llvm::function_ref>(VarDecl *)> - getInformation) { - if (auto *decl = - dyn_cast_or_null(resolvedOverload.choice.getDeclOrNull())) { - if (auto declInformation = getInformation(decl)) { - Type type; - VarDecl *memberDecl; - std::tie(memberDecl, type) = *declInformation; - if (Type baseType = resolvedOverload.choice.getBaseType()) { - type = baseType->getRValueType()->getTypeOfMember(memberDecl, type); - } - return std::make_pair(decl, type); - } - } - return std::nullopt; -} +static Type getPropertyWrapperTypeFromOverload( + ConstraintSystem &cs, SelectedOverload resolvedOverload, + llvm::function_ref getWrapperVar) { + auto *D = dyn_cast_or_null(resolvedOverload.choice.getDeclOrNull()); + if (!D) + return Type(); -std::optional> -ConstraintSystem::getPropertyWrapperProjectionInfo( - SelectedOverload resolvedOverload) { - return getPropertyWrapperInformationFromOverload( - resolvedOverload, DC, - [](VarDecl *decl) -> std::optional> { - if (!decl->hasAttachedPropertyWrapper()) - return std::nullopt; + auto *wrapperVar = getWrapperVar(D); + if (!wrapperVar) + return Type(); - auto projectionVar = decl->getPropertyWrapperProjectionVar(); - if (!projectionVar) - return std::nullopt; + // For the backing property we need to query the request to ensure it kicks + // type-checking if necessary. Otherwise we can query the interface type. + auto ty = wrapperVar == D->getPropertyWrapperBackingProperty() + ? D->getPropertyWrapperBackingPropertyType() + : wrapperVar->getInterfaceType(); + if (!ty) + return Type(); - return std::make_pair(projectionVar, - projectionVar->getInterfaceType()); - }); + // If this is a for a property, substitute the base type. + if (auto baseType = resolvedOverload.choice.getBaseType()) + ty = baseType->getRValueType()->getTypeOfMember(wrapperVar, ty); + + return ty; } -std::optional> -ConstraintSystem::getPropertyWrapperInformation( +Type ConstraintSystem::getPropertyWrapperProjectionType( SelectedOverload resolvedOverload) { - return getPropertyWrapperInformationFromOverload( - resolvedOverload, DC, - [](VarDecl *decl) -> std::optional> { - if (!decl->hasAttachedPropertyWrapper()) - return std::nullopt; - - auto backingTy = decl->getPropertyWrapperBackingPropertyType(); - if (!backingTy) - return std::nullopt; - - return std::make_pair(decl, backingTy); - }); + return getPropertyWrapperTypeFromOverload( + *this, resolvedOverload, + [&](VarDecl *decl) { return decl->getPropertyWrapperProjectionVar(); }); } -std::optional> -ConstraintSystem::getWrappedPropertyInformation( +Type ConstraintSystem::getPropertyWrapperBackingType( SelectedOverload resolvedOverload) { - return getPropertyWrapperInformationFromOverload( - resolvedOverload, DC, - [](VarDecl *decl) -> std::optional> { - if (auto wrapped = decl->getOriginalWrappedProperty()) - return std::make_pair(decl, wrapped->getInterfaceType()); + return getPropertyWrapperTypeFromOverload( + *this, resolvedOverload, + [](VarDecl *decl) { return decl->getPropertyWrapperBackingProperty(); }); +} - return std::nullopt; - }); +Type ConstraintSystem::getWrappedPropertyType( + SelectedOverload resolvedOverload) { + return getPropertyWrapperTypeFromOverload( + *this, resolvedOverload, + [](VarDecl *decl) { return decl->getOriginalWrappedProperty(); }); } void ConstraintSystem::addOverloadSet(Type boundType, diff --git a/test/decl/var/property_wrappers.swift b/test/decl/var/property_wrappers.swift index 48952ee744383..93314e470b2e5 100644 --- a/test/decl/var/property_wrappers.swift +++ b/test/decl/var/property_wrappers.swift @@ -1346,6 +1346,15 @@ struct MissingPropertyWrapperUnwrap { } } +_ = { + struct S { + @WrapperWithInitialValue var x = 0 + init() {} + } + func a(_: WrapperWithInitialValue) {} + a(S().x) // expected-error {{cannot convert value 'x' of type 'Int' to expected type 'WrapperWithInitialValue', use wrapper instead}} {{9-9=_}} +} + struct InvalidPropertyDelegateUse { // TODO(diagnostics): We need to a tailored diagnostic for extraneous arguments in property delegate initialization @Foo var x: Int = 42 // expected-error@:21 {{extra argument 'wrappedValue' in call}} From 260d10f6b8940269877b4265566e534c7bbbffda Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 26 Oct 2025 09:55:49 +0000 Subject: [PATCH 4/4] [CS] Fix a couple of `fixPropertyWrapperFailure` crashers Make sure we query the constraint system for a type if we have a local property wrapper in a closure to avoid kicking interface type computation outside the closure, and make sure we map into context if we need to. --- lib/Sema/ConstraintSystem.cpp | 18 +++++++++++++++--- ...onstraintSystem-getClosureType-36e4ea.swift | 2 +- ...eChecker-performTypoCorrection-e14553.swift | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/ConstraintSystem-getClosureType-36e4ea.swift (86%) rename validation-test/{compiler_crashers => compiler_crashers_fixed}/TypeChecker-performTypoCorrection-e14553.swift (90%) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index e8c9bb0e08ba5..aad7a277c0f5a 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1255,6 +1255,15 @@ static Type getPropertyWrapperTypeFromOverload( if (!wrapperVar) return Type(); + // First check to see if we have a type for this wrapper variable, which will + // the case for e.g local wrappers in closures. + if (auto ty = cs.getTypeIfAvailable(wrapperVar)) + return ty; + + // If we don't have a type for the wrapper variable this shouldn't be a + // VarDecl we're solving for. + ASSERT(!cs.hasType(D) && "Should have recorded type for wrapper var"); + // For the backing property we need to query the request to ensure it kicks // type-checking if necessary. Otherwise we can query the interface type. auto ty = wrapperVar == D->getPropertyWrapperBackingProperty() @@ -1263,10 +1272,13 @@ static Type getPropertyWrapperTypeFromOverload( if (!ty) return Type(); - // If this is a for a property, substitute the base type. - if (auto baseType = resolvedOverload.choice.getBaseType()) + // If this is a for a property, substitute the base type. Otherwise we have + // a local property wrapper and need to map the resulting type into context. + if (auto baseType = resolvedOverload.choice.getBaseType()) { ty = baseType->getRValueType()->getTypeOfMember(wrapperVar, ty); - + } else { + ty = cs.DC->mapTypeIntoContext(ty); + } return ty; } diff --git a/validation-test/compiler_crashers/ConstraintSystem-getClosureType-36e4ea.swift b/validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-36e4ea.swift similarity index 86% rename from validation-test/compiler_crashers/ConstraintSystem-getClosureType-36e4ea.swift rename to validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-36e4ea.swift index 43d11d74ee65f..abc5d6724ab71 100644 --- a/validation-test/compiler_crashers/ConstraintSystem-getClosureType-36e4ea.swift +++ b/validation-test/compiler_crashers_fixed/ConstraintSystem-getClosureType-36e4ea.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","signature":"swift::constraints::ConstraintSystem::getClosureType(swift::ClosureExpr const*) const","signatureAssert":"Assertion failed: (result), function getClosureType"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s @propertyWrapper struct a, swift::TypoCorrectionResults&, swift::GenericSignature, unsigned int)","signatureAssert":"Assertion failed: (!baseTypeOrNull || !baseTypeOrNull->hasTypeParameter() || genericSig), function performTypoCorrection"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s enum a