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/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) { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 7823676ee3e3b..aad7a277c0f5a 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1244,72 +1244,63 @@ 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; + // 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() + ? 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. 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; } -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/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/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}} 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 { + return + } + _x +} diff --git a/validation-test/compiler_crashers/TypeChecker-performTypoCorrection-e14553.swift b/validation-test/compiler_crashers_fixed/TypeChecker-performTypoCorrection-e14553.swift similarity index 90% rename from validation-test/compiler_crashers/TypeChecker-performTypoCorrection-e14553.swift rename to validation-test/compiler_crashers_fixed/TypeChecker-performTypoCorrection-e14553.swift index 8fa8def95197d..105ca2c280392 100644 --- a/validation-test/compiler_crashers/TypeChecker-performTypoCorrection-e14553.swift +++ b/validation-test/compiler_crashers_fixed/TypeChecker-performTypoCorrection-e14553.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","signature":"swift::TypeChecker::performTypoCorrection(swift::DeclContext*, swift::DeclRefKind, swift::Type, swift::optionset::OptionSet, 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