Skip to content

[5.5][Property Wrappers] Fix a bug where wrapped arguments were not properly coerced to the wrapper generator input type. #38892

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 23, 2021
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
9 changes: 5 additions & 4 deletions include/swift/AST/Initializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,16 @@ class PropertyWrapperInitializer : public Initializer {
};

private:
ParamDecl *param;
VarDecl *wrappedVar;
Kind kind;

public:
explicit PropertyWrapperInitializer(DeclContext *parent, ParamDecl *param, Kind kind)
explicit PropertyWrapperInitializer(DeclContext *parent, VarDecl *wrappedVar,
Kind kind)
: Initializer(InitializerKind::PropertyWrapper, parent),
param(param), kind(kind) {}
wrappedVar(wrappedVar), kind(kind) {}

ParamDecl *getParam() const { return param; }
VarDecl *getWrappedVar() const { return wrappedVar; }

Kind getKind() const { return kind; }

Expand Down
10 changes: 8 additions & 2 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,10 @@ class SolutionApplicationTarget {
static SolutionApplicationTarget forUninitializedWrappedVar(
VarDecl *wrappedVar);

/// Form a target for a synthesized property wrapper initializer.
static SolutionApplicationTarget forPropertyWrapperInitializer(
VarDecl *wrappedVar, DeclContext *dc, Expr *initializer);

Expr *getAsExpr() const {
switch (kind) {
case Kind::expression:
Expand Down Expand Up @@ -1802,7 +1806,7 @@ class SolutionApplicationTarget {
bool isOptionalSomePatternInit() const {
return kind == Kind::expression &&
expression.contextualPurpose == CTP_Initialization &&
isa<OptionalSomePattern>(expression.pattern) &&
dyn_cast_or_null<OptionalSomePattern>(expression.pattern) &&
!expression.pattern->isImplicit();
}

Expand All @@ -1826,7 +1830,9 @@ class SolutionApplicationTarget {

// Don't create property wrapper generator functions for static variables and
// local variables with initializers.
if (wrappedVar->isStatic() || wrappedVar->getDeclContext()->isLocalContext())
bool hasInit = expression.propertyWrapper.hasInitialWrappedValue;
if (wrappedVar->isStatic() ||
(hasInit && wrappedVar->getDeclContext()->isLocalContext()))
return false;

return expression.propertyWrapper.innermostWrappedValueInit == apply;
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2128,10 +2128,10 @@ void ASTMangler::appendContext(const DeclContext *ctx, StringRef useModuleName)
auto wrapperInit = cast<PropertyWrapperInitializer>(ctx);
switch (wrapperInit->getKind()) {
case PropertyWrapperInitializer::Kind::WrappedValue:
appendBackingInitializerEntity(wrapperInit->getParam());
appendBackingInitializerEntity(wrapperInit->getWrappedVar());
break;
case PropertyWrapperInitializer::Kind::ProjectedValue:
appendInitFromProjectedValueEntity(wrapperInit->getParam());
appendInitFromProjectedValueEntity(wrapperInit->getWrappedVar());
break;
}
return;
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ unsigned DeclContext::printContext(raw_ostream &OS, const unsigned indent,
}
case InitializerKind::PropertyWrapper: {
auto init = cast<PropertyWrapperInitializer>(this);
OS << "PropertyWrapper 0x" << (void*)init->getParam() << ", kind=";
OS << "PropertyWrapper 0x" << (void*)init->getWrappedVar() << ", kind=";
switch (init->getKind()) {
case PropertyWrapperInitializer::Kind::WrappedValue:
OS << "wrappedValue";
Expand Down
4 changes: 2 additions & 2 deletions lib/IDE/ExprContextAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1297,8 +1297,8 @@ class ExprContextAnalyzer {
auto AFD = dyn_cast<AbstractFunctionDecl>(initDC->getParent());
if (!AFD)
return;
auto *param = initDC->getParam();
recordPossibleType(AFD->mapTypeIntoContext(param->getInterfaceType()));
auto *var = initDC->getWrappedVar();
recordPossibleType(AFD->mapTypeIntoContext(var->getInterfaceType()));
break;
}
}
Expand Down
45 changes: 39 additions & 6 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6021,16 +6021,44 @@ Expr *ExprRewriter::coerceCallArguments(
};

if (paramInfo.hasExternalPropertyWrapper(paramIdx)) {
auto *param = getParameterAt(callee.getDecl(), paramIdx);
auto *paramDecl = getParameterAt(callee.getDecl(), paramIdx);
auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++];
auto wrapperType = solution.simplifyType(appliedWrapper.wrapperType);
auto initKind = appliedWrapper.initKind;

using ValueKind = AppliedPropertyWrapperExpr::ValueKind;
ValueKind valueKind = (initKind == PropertyWrapperInitKind::ProjectedValue ?
ValueKind::ProjectedValue : ValueKind::WrappedValue);
AppliedPropertyWrapperExpr::ValueKind valueKind;
PropertyWrapperValuePlaceholderExpr *generatorArg;
auto initInfo = paramDecl->getPropertyWrapperInitializerInfo();
if (initKind == PropertyWrapperInitKind::ProjectedValue) {
valueKind = AppliedPropertyWrapperExpr::ValueKind::ProjectedValue;
generatorArg = initInfo.getProjectedValuePlaceholder();
} else {
valueKind = AppliedPropertyWrapperExpr::ValueKind::WrappedValue;
generatorArg = initInfo.getWrappedValuePlaceholder();
}

// Coerce the property wrapper argument type to the input type of
// the property wrapper generator function. The wrapper generator
// has the same generic signature as the enclosing function, so we
// can use substitutions from the callee.
Type generatorInputType =
generatorArg->getType().subst(callee.getSubstitutions());
auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());

if (generatorArg->isAutoClosure()) {
auto *closureType = generatorInputType->castTo<FunctionType>();
arg = coerceToType(
arg, closureType->getResult(),
argLoc.withPathElement(ConstraintLocator::AutoclosureResult));
arg = cs.buildAutoClosureExpr(arg, closureType, dc);
}

arg = AppliedPropertyWrapperExpr::create(ctx, callee, param, arg->getStartLoc(),
arg = coerceToType(arg, generatorInputType, argLoc);

// Wrap the argument in an applied property wrapper expr, which will
// later turn into a call to the property wrapper generator function.
arg = AppliedPropertyWrapperExpr::create(ctx, callee, paramDecl,
arg->getStartLoc(),
wrapperType, arg, valueKind);
cs.cacheExprTypes(arg);
}
Expand Down Expand Up @@ -8404,13 +8432,18 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
// been subsumed by the backing property.
if (wrappedVar) {
ASTContext &ctx = cs.getASTContext();
wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0);
ctx.setSideCachedPropertyWrapperBackingPropertyType(
wrappedVar, initType->mapTypeOutOfContext());

// Record the semantic initializer on the outermost property wrapper.
wrappedVar->getAttachedPropertyWrappers().front()
->setSemanticInit(initializer);

// If this is a wrapped parameter, we're done.
if (isa<ParamDecl>(wrappedVar))
return resultTarget;

wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0);
}

// Coerce the pattern to the type of the initializer.
Expand Down
26 changes: 25 additions & 1 deletion lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5102,7 +5102,14 @@ SolutionApplicationTarget::SolutionApplicationTarget(
void SolutionApplicationTarget::maybeApplyPropertyWrapper() {
assert(kind == Kind::expression);
assert(expression.contextualPurpose == CTP_Initialization);
auto singleVar = expression.pattern->getSingleVar();

VarDecl *singleVar;
if (auto *pattern = expression.pattern) {
singleVar = pattern->getSingleVar();
} else {
singleVar = expression.propertyWrapper.wrappedVar;
}

if (!singleVar)
return;

Expand Down Expand Up @@ -5232,6 +5239,23 @@ SolutionApplicationTarget::forUninitializedWrappedVar(VarDecl *wrappedVar) {
return SolutionApplicationTarget(wrappedVar);
}

SolutionApplicationTarget
SolutionApplicationTarget::forPropertyWrapperInitializer(
VarDecl *wrappedVar, DeclContext *dc, Expr *initializer) {
SolutionApplicationTarget target(
initializer, dc, CTP_Initialization, wrappedVar->getType(),
/*isDiscarded=*/false);
target.expression.propertyWrapper.wrappedVar = wrappedVar;
if (auto *patternBinding = wrappedVar->getParentPatternBinding()) {
auto index = patternBinding->getPatternEntryIndexForVarDecl(wrappedVar);
target.expression.initialization.patternBinding = patternBinding;
target.expression.initialization.patternBindingIndex = index;
target.expression.pattern = patternBinding->getPattern(index);
}
target.maybeApplyPropertyWrapper();
return target;
}

ContextualPattern
SolutionApplicationTarget::getContextualPattern() const {
assert(kind == Kind::expression);
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ static void forEachOuterDecl(DeclContext *DC, Fn fn) {
case DeclContextKind::Initializer:
if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC))
fn(PBI->getBinding());
else if (auto *I = dyn_cast<PropertyWrapperInitializer>(DC))
fn(I->getWrappedVar());
break;

case DeclContextKind::SubscriptDecl:
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2597,8 +2597,8 @@ void swift::checkEnumElementActorIsolation(
}

void swift::checkPropertyWrapperActorIsolation(
PatternBindingDecl *binding, Expr *expr) {
ActorIsolationChecker checker(binding->getDeclContext());
VarDecl *wrappedVar, Expr *expr) {
ActorIsolationChecker checker(wrappedVar->getDeclContext());
expr->walk(checker);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ProtocolConformance;
class TopLevelCodeDecl;
class TypeBase;
class ValueDecl;
class VarDecl;

/// Add notes suggesting the addition of 'async' or '@asyncHandler', as
/// appropriate, to a diagnostic for a function that isn't an async context.
Expand All @@ -54,8 +55,7 @@ void checkTopLevelActorIsolation(TopLevelCodeDecl *decl);
void checkFunctionActorIsolation(AbstractFunctionDecl *decl);
void checkInitializerActorIsolation(Initializer *init, Expr *expr);
void checkEnumElementActorIsolation(EnumElementDecl *element, Expr *expr);
void checkPropertyWrapperActorIsolation(
PatternBindingDecl *binding, Expr *expr);
void checkPropertyWrapperActorIsolation(VarDecl *wrappedVar, Expr *expr);

/// Determine the isolation of a particular closure.
///
Expand Down
51 changes: 15 additions & 36 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2571,31 +2571,24 @@ static VarDecl *synthesizePropertyWrapperProjectionVar(

static void typeCheckSynthesizedWrapperInitializer(VarDecl *wrappedVar,
Expr *&initializer) {
// Figure out the context in which the initializer was written.
auto *parentPBD = wrappedVar->getParentPatternBinding();
auto i = parentPBD->getPatternEntryIndexForVarDecl(wrappedVar);
DeclContext *originalDC = parentPBD->getDeclContext();
if (!originalDC->isLocalContext()) {
auto initContext =
cast_or_null<PatternBindingInitializer>(parentPBD->getInitContext(i));
if (initContext)
originalDC = initContext;
}
auto *dc = wrappedVar->getInnermostDeclContext();
auto &ctx = wrappedVar->getASTContext();
auto *initContext = new (ctx) PropertyWrapperInitializer(
dc, wrappedVar, PropertyWrapperInitializer::Kind::WrappedValue);

// Type-check the initialization.
auto *pattern = parentPBD->getPattern(i);
TypeChecker::typeCheckBinding(pattern, initializer, originalDC,
wrappedVar->getType(), parentPBD, i);
using namespace constraints;
auto target = SolutionApplicationTarget::forPropertyWrapperInitializer(
wrappedVar, initContext, initializer);
auto result = TypeChecker::typeCheckExpression(target);
if (!result)
return;

if (auto initializerContext =
dyn_cast_or_null<Initializer>(parentPBD->getInitContext(i))) {
TypeChecker::contextualizeInitializer(initializerContext, initializer);
}
initializer = result->getAsExpr();

auto *backingVar = wrappedVar->getPropertyWrapperBackingProperty();
auto *backingPBD = backingVar->getParentPatternBinding();
checkPropertyWrapperActorIsolation(backingPBD, initializer);
TypeChecker::checkPropertyWrapperEffects(backingPBD, initializer);
TypeChecker::contextualizeInitializer(initContext, initializer);
checkPropertyWrapperActorIsolation(wrappedVar, initializer);
TypeChecker::checkInitializerEffects(initContext, initializer);
}

static PropertyWrapperMutability::Value
Expand Down Expand Up @@ -2954,21 +2947,7 @@ PropertyWrapperInitializerInfoRequest::evaluate(Evaluator &evaluator,
!var->getName().hasDollarPrefix()) {
wrappedValueInit = PropertyWrapperValuePlaceholderExpr::create(
ctx, var->getSourceRange(), var->getType(), /*wrappedValue=*/nullptr);

if (auto *param = dyn_cast<ParamDecl>(var)) {
wrappedValueInit = buildPropertyWrapperInitCall(
var, storageType, wrappedValueInit, PropertyWrapperInitKind::WrappedValue);
TypeChecker::typeCheckExpression(wrappedValueInit, dc);

// Check initializer effects.
auto *initContext = new (ctx) PropertyWrapperInitializer(
dc, param, PropertyWrapperInitializer::Kind::WrappedValue);
TypeChecker::contextualizeInitializer(initContext, wrappedValueInit);
checkInitializerActorIsolation(initContext, wrappedValueInit);
TypeChecker::checkInitializerEffects(initContext, wrappedValueInit);
} else {
typeCheckSynthesizedWrapperInitializer(var, wrappedValueInit);
}
typeCheckSynthesizedWrapperInitializer(var, wrappedValueInit);
}

return PropertyWrapperInitializerInfo(wrappedValueInit, projectedValueInit);
Expand Down
39 changes: 39 additions & 0 deletions test/SILGen/property_wrapper_parameter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func simpleWrapperParameterCaller(projection: Projection<Int>) {

testSimpleWrapperParameter($value: projection)
// CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfW : $@convention(thin) (Projection<Int>) -> Wrapper<Int>

var x: Int = 10
testSimpleWrapperParameter(value: x)
// CHECK: function_ref @$s26property_wrapper_parameter26testSimpleWrapperParameter5valueyAA0F0VySiG_tFACL_SivpfP : $@convention(thin) (Int) -> Wrapper<Int>
}

// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlF : $@convention(thin) <T> (@in_guaranteed Wrapper<T>) -> ()
Expand All @@ -71,6 +75,41 @@ func genericWrapperCaller(projection: Projection<Int>) {

testGenericWrapper($value: projection)
// CHECK: function_ref @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfW : $@convention(thin) <τ_0_0> (@in Projection<τ_0_0>) -> @out Wrapper<τ_0_0>

var x: Int = 10
testGenericWrapper(value: x)
// CHECK: function_ref @$s26property_wrapper_parameter18testGenericWrapper5valueyAA0F0VyxG_tlFACL_xvpfP : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out Wrapper<τ_0_0>
}

@propertyWrapper
public struct AutoClosureWrapper<T> {
public var wrappedValue: T

// CHECK-LABEL: sil [ossa] @$s26property_wrapper_parameter18AutoClosureWrapperV12wrappedValueACyxGxyXK_tcfC : $@convention(method) <T> (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <T>, @thin AutoClosureWrapper<T>.Type) -> @out AutoClosureWrapper<T>
public init(wrappedValue: @autoclosure () -> T) {
self.wrappedValue = wrappedValue()
}

public var projectedValue: Projection<T> {
Projection(wrappedValue: wrappedValue)
}

public init(projectedValue: Projection<T>) {
self.wrappedValue = projectedValue.wrappedValue
}
}

// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter22testAutoClosureWrapper5valueyAA0efG0VyxG_tlF : $@convention(thin) <T> (@in_guaranteed AutoClosureWrapper<T>) -> ()
func testAutoClosureWrapper<T>(@AutoClosureWrapper value: T) {
// property wrapper backing initializer of value #1 in testAutoClosureWrapper<A>(value:)
// CHECK-LABEL: sil private [ossa] @$s26property_wrapper_parameter22testAutoClosureWrapper5valueyAA0efG0VyxG_tlFACL_xvpfP : $@convention(thin) <T> (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <T>) -> @out AutoClosureWrapper<T>
// CHECK: function_ref @$s26property_wrapper_parameter18AutoClosureWrapperV12wrappedValueACyxGxyXK_tcfC : $@convention(method) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <τ_0_0>, @thin AutoClosureWrapper<τ_0_0>.Type) -> @out AutoClosureWrapper<τ_0_0>
}

// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter24autoClosureWrapperCalleryyF : $@convention(thin) () -> ()
func autoClosureWrapperCaller() {
testAutoClosureWrapper(value: 10)
// CHECK: function_ref @$s26property_wrapper_parameter22testAutoClosureWrapper5valueyAA0efG0VyxG_tlFACL_xvpfP : $@convention(thin) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <τ_0_0>) -> @out AutoClosureWrapper<τ_0_0>
}

// CHECK-LABEL: sil hidden [ossa] @$s26property_wrapper_parameter33testSimpleClosureWrapperParameteryyF : $@convention(thin) () -> ()
Expand Down