Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::pair<VarDecl *, Type>>
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<std::pair<VarDecl *, Type>>
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<std::pair<VarDecl *, Type>>
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.
///
Expand Down
35 changes: 17 additions & 18 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4825,14 +4825,19 @@ static ConstraintFix *fixPropertyWrapperFailure(
if (!resolvedOverload)
return nullptr;

auto *decl = resolvedOverload->choice.getDeclOrNull();
auto *VD = dyn_cast_or_null<VarDecl>(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))
Expand All @@ -4841,40 +4846,34 @@ static ConstraintFix *fixPropertyWrapperFailure(
if (baseTy->is<TypeVariableType>() || type->is<TypeVariableType>())
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;
}

Expand Down
53 changes: 23 additions & 30 deletions lib/Sema/CSSyntacticElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,36 +141,6 @@ class TypeVariableRefFinder : public ASTWalker {
if (!var)
return Action::Continue(expr);

if (auto *wrappedVar = var->getOriginalWrappedProperty()) {
Copy link
Contributor

@xedin xedin Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to remember why I did it this way and failing, we might have done something differently back then...

// 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()) {
// $<name> is the projected value var
CS.setType(var, computeProjectedValueType(wrappedVar, wrapperType));
} else {
// _<name> 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.
Expand Down Expand Up @@ -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<LocatorPathElt::PatternBindingElement>());

Expand Down Expand Up @@ -847,6 +837,9 @@ class SyntacticElementConstraintGenerator
hadError = true;
return;
}

// Set the types of any auxiliary property wrapper vars.
setPropertyWrapperAuxiliaryTypes(*target);
}

void visitDecl(Decl *decl) {
Expand Down
103 changes: 47 additions & 56 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1244,72 +1244,63 @@ TypeVariableType *ConstraintSystem::isRepresentativeFor(
return *member;
}

static std::optional<std::pair<VarDecl *, Type>>
getPropertyWrapperInformationFromOverload(
SelectedOverload resolvedOverload, DeclContext *DC,
llvm::function_ref<std::optional<std::pair<VarDecl *, Type>>(VarDecl *)>
getInformation) {
if (auto *decl =
dyn_cast_or_null<VarDecl>(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<VarDecl *(VarDecl *)> getWrapperVar) {
auto *D = dyn_cast_or_null<VarDecl>(resolvedOverload.choice.getDeclOrNull());
if (!D)
return Type();

std::optional<std::pair<VarDecl *, Type>>
ConstraintSystem::getPropertyWrapperProjectionInfo(
SelectedOverload resolvedOverload) {
return getPropertyWrapperInformationFromOverload(
resolvedOverload, DC,
[](VarDecl *decl) -> std::optional<std::pair<VarDecl *, Type>> {
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<std::pair<VarDecl *, Type>>
ConstraintSystem::getPropertyWrapperInformation(
Type ConstraintSystem::getPropertyWrapperProjectionType(
SelectedOverload resolvedOverload) {
return getPropertyWrapperInformationFromOverload(
resolvedOverload, DC,
[](VarDecl *decl) -> std::optional<std::pair<VarDecl *, Type>> {
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<std::pair<VarDecl *, Type>>
ConstraintSystem::getWrappedPropertyInformation(
Type ConstraintSystem::getPropertyWrapperBackingType(
SelectedOverload resolvedOverload) {
return getPropertyWrapperInformationFromOverload(
resolvedOverload, DC,
[](VarDecl *decl) -> std::optional<std::pair<VarDecl *, Type>> {
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,
Expand Down
27 changes: 22 additions & 5 deletions lib/Sema/SyntacticElementTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<VarDecl>(D)) {
// Only set invalid if we don't already have an interface type computed.
if (!VD->hasInterfaceType())
D->setInvalid();
} else if (isa<PatternBindingDecl>(D)) {
D->setInvalid();
invalidateVarDecl(VD);
} else if (auto *PBD = dyn_cast<PatternBindingDecl>(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<PatternBindingDecl>(D));
}
Expand Down
9 changes: 9 additions & 0 deletions test/decl/var/property_wrappers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,15 @@ struct MissingPropertyWrapperUnwrap {
}
}

_ = {
struct S {
@WrapperWithInitialValue var x = 0
init() {}
}
func a<T>(_: WrapperWithInitialValue<T>) {}
a(S().x) // expected-error {{cannot convert value 'x' of type 'Int' to expected type 'WrapperWithInitialValue<Int>', 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}}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<b {
wrappedValue: b
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// {"kind":"typecheck","signature":"swift::TypeChecker::performTypoCorrection(swift::DeclContext*, swift::DeclRefKind, swift::Type, swift::optionset::OptionSet<swift::NameLookupFlags, unsigned int>, 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<b {
@propertyWrapper class e {wrappedValue:b var projectedValue: a? {
@e var wrappedValue: b wrappedValue.d