[CS] Use adjusted type for single curry thunks

This matches the double curry thunk logic and
ensures that the resulting autoclosure matches the
expected type of the reference, avoiding mismatches
with parent expressions.

rdar://140212823
This commit is contained in:
Hamish Knight
2024-11-21 12:14:00 +00:00
parent 683c7ceb31
commit 68648f74ef
5 changed files with 119 additions and 45 deletions

View File

@@ -759,22 +759,27 @@ namespace {
new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType); new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType);
cs.cacheType(declRefExpr); cs.cacheType(declRefExpr);
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind()); declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
Expr *result = adjustTypeForDeclReference(
declRefExpr, fullType, adjustedFullType, locator);
// If we have to load, do so now.
if (loadImmediately)
result = cs.addImplicitLoadExpr(result);
result = forceUnwrapIfExpected(result, locator); Expr *result = forceUnwrapIfExpected(declRefExpr, locator);
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) { if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() && if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() &&
(declRefExpr->getFunctionRefKind() == FunctionRefKind::Compound || (declRefExpr->getFunctionRefKind() == FunctionRefKind::Compound ||
declRefExpr->getFunctionRefKind() == FunctionRefKind::Unapplied)) { declRefExpr->getFunctionRefKind() == FunctionRefKind::Unapplied)) {
result = buildSingleCurryThunk(result, fnDecl, locator); // We don't need to do any further adjustment once we've built the
// curry thunk.
return buildSingleCurryThunk(result, fnDecl,
adjustedFullType->castTo<FunctionType>(),
locator);
} }
} }
result = adjustTypeForDeclReference(result, fullType, adjustedFullType,
locator);
// If we have to load, do so now.
if (loadImmediately)
result = cs.addImplicitLoadExpr(result);
return result; return result;
} }
@@ -1406,41 +1411,20 @@ namespace {
/// parameters. /// parameters.
/// \param declOrClosure The underlying function-like declaration or /// \param declOrClosure The underlying function-like declaration or
/// closure we're going to call. /// closure we're going to call.
/// \param thunkTy The type of the resulting thunk. This should be the
/// type of the \c fnExpr, with any potential adjustments for things like
/// concurrency.
/// \param locator The locator pinned on the function reference carried /// \param locator The locator pinned on the function reference carried
/// by \p fnExpr. If the function has associated applied property wrappers, /// by \p fnExpr. If the function has associated applied property wrappers,
/// the locator is used to pull them in. /// the locator is used to pull them in.
AutoClosureExpr *buildSingleCurryThunk(Expr *fnExpr, AutoClosureExpr *buildSingleCurryThunk(Expr *fnExpr,
DeclContext *declOrClosure, DeclContext *declOrClosure,
FunctionType *thunkTy,
ConstraintLocatorBuilder locator) { ConstraintLocatorBuilder locator) {
auto *const thunkTy = cs.getType(fnExpr)->castTo<FunctionType>();
return buildSingleCurryThunk(/*baseExpr=*/nullptr, fnExpr, declOrClosure, return buildSingleCurryThunk(/*baseExpr=*/nullptr, fnExpr, declOrClosure,
thunkTy, locator); thunkTy, locator);
} }
/// Build a "{ args in base.fn(args) }" single-expression curry thunk.
///
/// \param baseExpr The base expression to be captured.
/// \param fnExpr The expression to be called by consecutively applying
/// the \p baseExpr and thunk parameters.
/// \param declOrClosure The underlying function-like declaration or
/// closure we're going to call.
/// \param locator The locator pinned on the function reference carried
/// by \p fnExpr. If the function has associated applied property wrappers,
/// the locator is used to pull them in.
AutoClosureExpr *buildSingleCurryThunk(Expr *baseExpr, Expr *fnExpr,
DeclContext *declOrClosure,
ConstraintLocatorBuilder locator) {
assert(baseExpr);
auto *const thunkTy = cs.getType(fnExpr)
->castTo<FunctionType>()
->getResult()
->castTo<FunctionType>();
return buildSingleCurryThunk(baseExpr, fnExpr, declOrClosure, thunkTy,
locator);
}
/// Build a "{ self in { args in self.fn(args) } }" nested curry thunk. /// Build a "{ self in { args in self.fn(args) } }" nested curry thunk.
/// ///
/// \param memberRef The expression to be called in the inner thunk by /// \param memberRef The expression to be called in the inner thunk by
@@ -1909,9 +1893,10 @@ namespace {
// have side effects, instead of abstracting out a 'self' parameter. // have side effects, instead of abstracting out a 'self' parameter.
const auto isSuperPartialApplication = needsCurryThunk && isSuper; const auto isSuperPartialApplication = needsCurryThunk && isSuper;
if (isSuperPartialApplication) { if (isSuperPartialApplication) {
ref = buildSingleCurryThunk(base, declRefExpr, ref = buildSingleCurryThunk(
cast<AbstractFunctionDecl>(member), base, declRefExpr, cast<AbstractFunctionDecl>(member),
memberLocator); adjustedOpenedType->castTo<FunctionType>(),
memberLocator);
} else if (needsCurryThunk) { } else if (needsCurryThunk) {
// Another case where we want to build a single closure is when // Another case where we want to build a single closure is when
// we have a partial application of a static member. It is better // we have a partial application of a static member. It is better
@@ -1927,14 +1912,13 @@ namespace {
cs.getType(base)); cs.getType(base));
cs.setType(base, base->getType()); cs.setType(base, base->getType());
auto *closure = buildSingleCurryThunk(
base, declRefExpr, cast<AbstractFunctionDecl>(member),
memberLocator);
// Skip the code below -- we're not building an extra level of // Skip the code below -- we're not building an extra level of
// call by applying the metatype; instead, the closure we just // call by applying the metatype; instead, the closure we just
// built is the curried reference. // built is the curried reference.
return closure; return buildSingleCurryThunk(
base, declRefExpr, cast<AbstractFunctionDecl>(member),
adjustedOpenedType->castTo<FunctionType>(),
memberLocator);
} else { } else {
// Add a useless ".self" to avoid downstream diagnostics, in case // Add a useless ".self" to avoid downstream diagnostics, in case
// the type ref is still a TypeExpr. // the type ref is still a TypeExpr.
@@ -8874,8 +8858,10 @@ namespace {
rewriteFunction(closure); rewriteFunction(closure);
if (AnyFunctionRef(closure).hasExternalPropertyWrapperParameters()) { if (AnyFunctionRef(closure).hasExternalPropertyWrapperParameters()) {
auto *thunkTy = Rewriter.cs.getType(closure)->castTo<FunctionType>();
return Action::SkipNode(Rewriter.buildSingleCurryThunk( return Action::SkipNode(Rewriter.buildSingleCurryThunk(
closure, closure, Rewriter.cs.getConstraintLocator(closure))); closure, closure, thunkTy,
Rewriter.cs.getConstraintLocator(closure)));
} }
return Action::SkipNode(closure); return Action::SkipNode(closure);

View File

@@ -0,0 +1,17 @@
// RUN: %target-swift-emit-silgen -verify -disable-objc-attr-requires-foundation-module %s
// RUN: %target-swift-emit-silgen -verify -swift-version 6 -disable-objc-attr-requires-foundation-module %s
// REQUIRES: objc_interop
@objc class C {
@preconcurrency @objc func foo(_ x: Sendable) {}
}
func bar(_ fn: (Any) -> Void) {}
func bar(_ fn: (Sendable) -> Void) {}
// Make sure we can handle both the implicit unwrap and concurrency adjustment.
func foo(_ x: AnyObject) {
bar(x.foo)
let _ = AnyObject.foo
}

View File

@@ -0,0 +1,29 @@
// RUN: %target-swift-emit-silgen %s -verify -swift-version 6
// rdar://140212823 - Make sure we build curry thunks using the adjusted
// reference type, such that the ParenExpr agrees with the type.
class C: @unchecked Sendable {
func foo() {}
}
class D: C, @unchecked Sendable {
func bar() {
let _ = (super.foo)
}
}
struct S {
func instanceMethod() {}
func foo() {
let _ = (self.instanceMethod)
}
static func staticMethod() {}
}
let _ = (S.instanceMethod)
let _ = (type(of: S()).instanceMethod)
let _ = (S.staticMethod)
let _ = (type(of: S()).staticMethod)
let _: (Int, Int) -> Int = (+)

View File

@@ -362,7 +362,7 @@ class Base {
class Derived : Base { class Derived : Base {
// CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC9superCallyyF : $@convention(method) (@guaranteed Derived) -> () // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC9superCallyyF : $@convention(method) (@guaranteed Derived) -> ()
// CHECK: convert_function %{{[0-9]+}} : $@callee_guaranteed () -> @owned Base to $@callee_guaranteed () -> @owned Derived // CHECK: function_ref @$s12dynamic_self7DerivedC9superCallyyFACycfu_ : $@convention(thin) (@guaranteed Derived) -> @owned Derived
// CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived // CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK-NOT: unchecked_ref_cast // CHECK-NOT: unchecked_ref_cast
// CHECK: end sil function '$s12dynamic_self7DerivedC9superCallyyF' // CHECK: end sil function '$s12dynamic_self7DerivedC9superCallyyF'
@@ -372,9 +372,12 @@ class Derived : Base {
_ = super.property _ = super.property
_ = super[] _ = super[]
} }
// CHECK-LABEL: sil private [ossa] @$s12dynamic_self7DerivedC9superCallyyFACycfu_ : $@convention(thin) (@guaranteed Derived) -> @owned Derived
// CHECK: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK: end sil function '$s12dynamic_self7DerivedC9superCallyyFACycfu_'
// CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC15superCallStaticyyFZ : $@convention(method) (@thick Derived.Type) -> () // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC15superCallStaticyyFZ : $@convention(method) (@thick Derived.Type) -> ()
// CHECK: convert_function %{{[0-9]+}} : $@callee_guaranteed () -> @owned Base to $@callee_guaranteed () -> @owned Derived // CHECK: function_ref @$s12dynamic_self7DerivedC15superCallStaticyyFZACycfu_ : $@convention(thin) (@thick Derived.Type) -> @owned Derived
// CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived // CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK-NOT: unchecked_ref_cast // CHECK-NOT: unchecked_ref_cast
// CHECK: end sil function '$s12dynamic_self7DerivedC15superCallStaticyyFZ' // CHECK: end sil function '$s12dynamic_self7DerivedC15superCallStaticyyFZ'
@@ -384,9 +387,12 @@ class Derived : Base {
_ = super.staticProperty _ = super.staticProperty
_ = super[] _ = super[]
} }
// CHECK-LABEL: sil private [ossa] @$s12dynamic_self7DerivedC15superCallStaticyyFZACycfu_ : $@convention(thin) (@thick Derived.Type) -> @owned Derived
// CHECK: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK: end sil function '$s12dynamic_self7DerivedC15superCallStaticyyFZACycfu_'
// CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyF : $@convention(method) (@guaranteed Derived) -> @owned Derived // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyF : $@convention(method) (@guaranteed Derived) -> @owned Derived
// CHECK: convert_function %{{[0-9]+}} : $@callee_guaranteed () -> @owned Base to $@callee_guaranteed () -> @owned Derived // CHECK: function_ref @$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyFACXDycfu_ : $@convention(thin) (@guaranteed Derived) -> @owned Derived
// CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived // CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK-NOT: unchecked_ref_cast // CHECK-NOT: unchecked_ref_cast
// CHECK: end sil function '$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyF' // CHECK: end sil function '$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyF'
@@ -396,9 +402,12 @@ class Derived : Base {
_ = super[] _ = super[]
return super.property return super.property
} }
// CHECK-LABEL: sil private [ossa] @$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyFACXDycfu_ : $@convention(thin) (@guaranteed Derived) -> @owned Derived
// CHECK: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK: end sil function '$s12dynamic_self7DerivedC32superCallFromMethodReturningSelfACXDyFACXDycfu_'
// CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZ : $@convention(method) (@thick Derived.Type) -> @owned Derived // CHECK-LABEL: sil hidden [ossa] @$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZ : $@convention(method) (@thick Derived.Type) -> @owned Derived
// CHECK: convert_function %{{[0-9]+}} : $@callee_guaranteed () -> @owned Base to $@callee_guaranteed () -> @owned Derived // CHECK: function_ref @$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZACXDycfu_ : $@convention(thin) (@thick @dynamic_self Derived.Type) -> @owned Derived
// CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived // CHECK-COUNT-3: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK-NOT: unchecked_ref_cast // CHECK-NOT: unchecked_ref_cast
// CHECK: end sil function '$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZ' // CHECK: end sil function '$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZ'
@@ -408,6 +417,9 @@ class Derived : Base {
_ = super[] _ = super[]
return super.staticProperty return super.staticProperty
} }
// CHECK-LABEL: sil private [ossa] @$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZACXDycfu_ : $@convention(thin) (@thick @dynamic_self Derived.Type) -> @owned Derived
// CHECK: unchecked_ref_cast %{{[0-9]+}} : $Base to $Derived
// CHECK: end sil function '$s12dynamic_self7DerivedC38superCallFromMethodReturningSelfStaticACXDyFZACXDycfu_'
} }
class Generic<T> { class Generic<T> {

View File

@@ -37,3 +37,33 @@ func test(from request: Request) {
// for preconcurrency // for preconcurrency
let _ = request.identifier let _ = request.identifier
} }
@propertyWrapper
struct Wrapper<T> {
var wrappedValue: T
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
init(projectedValue: Self) {
self = projectedValue
}
var projectedValue: Self { self }
}
// rdar://140212823 - Make sure we can handle the Sendable promotion of `y` in
// the curry thunk.
@preconcurrency func hasWrapperAndPreconcurrency(@Wrapper _ x: Int, _ y: Sendable) {}
struct HasWrapperAndPreconcurrency {
@preconcurrency func instanceMethod(@Wrapper _ x: Int, _ y: Sendable) {}
@preconcurrency static func staticMethod(@Wrapper _ x: Int, _ y: Sendable) {}
}
func testPropertyWrapperPreconcurrencyThunk(_ x: HasWrapperAndPreconcurrency) {
let fn = (hasWrapperAndPreconcurrency)
fn(0, C())
_ = (HasWrapperAndPreconcurrency.staticMethod)
_ = (type(of: x).staticMethod)
_ = (x.instanceMethod)
_ = (type(of: x).instanceMethod)
}