Skip to content

Commit c50a656

Browse files
authored
Merge pull request #77983 from hamishknight/ref-wrap
[CS] Correctly handle compound-applied functions with property wrappers
2 parents 5db0f83 + 48dc186 commit c50a656

File tree

8 files changed

+93
-41
lines changed

8 files changed

+93
-41
lines changed

include/swift/AST/Expr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
552552
/// \c nullptr.
553553
ArgumentList *getArgs() const;
554554

555+
/// If the expression has a DeclNameLoc, returns it. Otherwise, returns
556+
/// an nullp DeclNameLoc.
557+
DeclNameLoc getNameLoc() const;
558+
555559
/// Produce a mapping from each subexpression to its parent
556560
/// expression, with the provided expression serving as the root of
557561
/// the parent map.

lib/AST/Expr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,25 @@ ArgumentList *Expr::getArgs() const {
859859
return nullptr;
860860
}
861861

862+
DeclNameLoc Expr::getNameLoc() const {
863+
if (auto *DRE = dyn_cast<DeclRefExpr>(this))
864+
return DRE->getNameLoc();
865+
if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(this))
866+
return UDRE->getNameLoc();
867+
if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(this))
868+
return ODRE->getNameLoc();
869+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(this))
870+
return UDE->getNameLoc();
871+
if (auto *UME = dyn_cast<UnresolvedMemberExpr>(this))
872+
return UME->getNameLoc();
873+
if (auto *MRE = dyn_cast<MemberRefExpr>(this))
874+
return MRE->getNameLoc();
875+
if (auto *DRME = dyn_cast<DynamicMemberRefExpr>(this))
876+
return DRME->getNameLoc();
877+
878+
return DeclNameLoc();
879+
}
880+
862881
llvm::DenseMap<Expr *, Expr *> Expr::getParentMap() {
863882
class RecordingTraversal : public ASTWalker {
864883
public:

lib/IDE/CursorInfo.cpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -214,23 +214,6 @@ class NodeFinder : ASTWalker {
214214
return Action::Continue();
215215
}
216216

217-
/// Retrieve the name location for an expression that supports cursor info.
218-
DeclNameLoc getExprNameLoc(Expr *E) {
219-
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
220-
return DRE->getNameLoc();
221-
222-
if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(E))
223-
return UDRE->getNameLoc();
224-
225-
if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(E))
226-
return ODRE->getNameLoc();
227-
228-
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E))
229-
return UDE->getNameLoc();
230-
231-
return DeclNameLoc();
232-
}
233-
234217
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
235218
if (auto closure = dyn_cast<ClosureExpr>(E)) {
236219
DeclContextStack.push_back(closure);
@@ -247,7 +230,7 @@ class NodeFinder : ASTWalker {
247230
}
248231
}
249232

250-
if (getExprNameLoc(E).getBaseNameLoc() != LocToResolve)
233+
if (E->getNameLoc().getBaseNameLoc() != LocToResolve)
251234
return Action::Continue(E);
252235

253236
assert(Result == nullptr);

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -764,10 +764,7 @@ namespace {
764764

765765
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
766766
if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() &&
767-
// FIXME(FunctionRefInfo): This should just be `isUnapplied()`, see
768-
// the FIXME in `unwrapPropertyWrapperParameterTypes`.
769-
(declRefExpr->getFunctionRefInfo().isCompoundName() ||
770-
declRefExpr->getFunctionRefInfo().isUnappliedBaseName())) {
767+
declRefExpr->getFunctionRefInfo().isUnapplied()) {
771768
// We don't need to do any further adjustment once we've built the
772769
// curry thunk.
773770
return buildSingleCurryThunk(result, fnDecl,

lib/Sema/CSSimplify.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,17 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
17251725
continue;
17261726
}
17271727

1728+
// See if we have a parameter label specified in the function's DeclNameLoc.
1729+
Identifier compoundParamLabel;
1730+
if (auto *E = getAsExpr(calleeLocator->getAnchor())) {
1731+
auto nameLoc = E->getNameLoc();
1732+
if (auto labelLoc = nameLoc.getArgumentLabelLoc(paramIdx)) {
1733+
auto &ctx = cs.getASTContext();
1734+
auto labelTok = Lexer::getTokenAtLocation(ctx.SourceMgr, labelLoc);
1735+
compoundParamLabel = ctx.getIdentifier(labelTok.getText());
1736+
}
1737+
}
1738+
17281739
// Compare each of the bound arguments for this parameter.
17291740
for (auto argIdx : parameterBindings[paramIdx]) {
17301741
auto loc = locator.withPathElement(LocatorPathElt::ApplyArgToParam(
@@ -1813,14 +1824,17 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
18131824
openedExistentials.push_back({openedTypeVar, opened});
18141825
}
18151826

1816-
auto argLabel = argument.getLabel();
1827+
// If we have a compound function reference (e.g `fn($x:)`), respect
1828+
// the parameter label given. Otherwise look at the argument label.
1829+
auto wrapperArgLabel = compoundParamLabel.empty() ? argument.getLabel()
1830+
: compoundParamLabel;
18171831
if (paramInfo.hasExternalPropertyWrapper(paramIdx) ||
1818-
argLabel.hasDollarPrefix()) {
1832+
wrapperArgLabel.hasDollarPrefix()) {
18191833
auto *param = getParameterAt(callee, paramIdx);
18201834
assert(param);
18211835
if (cs.applyPropertyWrapperToParameter(paramTy, argTy,
18221836
const_cast<ParamDecl *>(param),
1823-
argLabel, subKind, loc)
1837+
wrapperArgLabel, subKind, loc)
18241838
.isFailure()) {
18251839
return cs.getTypeMatchFailure(loc);
18261840
}

lib/Sema/TypeOfReference.cpp

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -694,13 +694,8 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl *
694694
FunctionRefInfo functionRefInfo, FunctionType *functionType,
695695
ConstraintLocatorBuilder locator) {
696696
// Only apply property wrappers to unapplied references to functions.
697-
// FIXME(FunctionRefInfo): This should just be `isUnapplied()`, which would
698-
// fix https://github.com/swiftlang/swift/issues/77823, but we also need to
699-
// correctly handle the wrapping in matchCallArguments.
700-
if (!(functionRefInfo.isCompoundName() ||
701-
functionRefInfo.isUnappliedBaseName())) {
697+
if (!functionRefInfo.isUnapplied())
702698
return functionType;
703-
}
704699

705700
// This transform is not applicable to pattern matching context.
706701
//
@@ -714,16 +709,8 @@ unwrapPropertyWrapperParameterTypes(ConstraintSystem &cs, AbstractFunctionDecl *
714709
SmallVector<AnyFunctionType::Param, 4> adjustedParamTypes;
715710

716711
DeclNameLoc nameLoc;
717-
auto *ref = getAsExpr(locator.getAnchor());
718-
if (auto *declRef = dyn_cast<DeclRefExpr>(ref)) {
719-
nameLoc = declRef->getNameLoc();
720-
} else if (auto *dotExpr = dyn_cast<UnresolvedDotExpr>(ref)) {
721-
nameLoc = dotExpr->getNameLoc();
722-
} else if (auto *overloadedRef = dyn_cast<OverloadedDeclRefExpr>(ref)) {
723-
nameLoc = overloadedRef->getNameLoc();
724-
} else if (auto *memberExpr = dyn_cast<UnresolvedMemberExpr>(ref)) {
725-
nameLoc = memberExpr->getNameLoc();
726-
}
712+
if (auto *ref = getAsExpr(locator.getAnchor()))
713+
nameLoc = ref->getNameLoc();
727714

728715
for (unsigned i : indices(*paramList)) {
729716
Identifier argLabel;

test/Sema/property_wrapper_parameter.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,40 @@ struct ProjectionWrapper<Value> {
159159
}
160160
}
161161

162+
// https://github.com/swiftlang/swift/issues/77823
163+
// Make sure we correctly handle compound applied functions.
164+
func testCompoundApplication() {
165+
func foo(@ProjectionWrapper x: Int) {}
166+
struct HasProjectionWrapperMember {
167+
static func foo(@ProjectionWrapper x: Int) {}
168+
}
169+
170+
foo(x:)(0)
171+
foo($x:)(ProjectionWrapper(wrappedValue: 0))
172+
173+
(foo($x:).self)(ProjectionWrapper(wrappedValue: 0))
174+
HasProjectionWrapperMember.foo($x:)(ProjectionWrapper(wrappedValue: 0))
175+
176+
foo(x:)(ProjectionWrapper(wrappedValue: 0)) // expected-error {{cannot convert value of type 'ProjectionWrapper<Int>' to expected argument type 'Int'}}
177+
foo(x:)(ProjectionWrapper(wrappedValue: "")) // expected-error {{cannot convert value of type 'ProjectionWrapper<String>' to expected argument type 'Int'}}
178+
foo(x:)("") // expected-error {{cannot convert value of type 'String' to expected argument type 'Int'}}
179+
180+
foo($x:)(ProjectionWrapper(wrappedValue: "")) // expected-error {{cannot convert value of type 'String' to expected argument type 'Int'}}
181+
foo($x:)(0) // expected-error {{cannot convert value of type 'Int' to expected argument type 'ProjectionWrapper<Int>'}}
182+
foo($x:)("") // expected-error {{cannot convert value of type 'String' to expected argument type 'ProjectionWrapper<Int>'}}
183+
184+
func bar(x: Int) {} // expected-note 2{{parameter 'x' does not have an attached property wrapper}}
185+
bar($x:)(0) // expected-error {{cannot use property wrapper projection argument}}
186+
_ = bar($x:) // expected-error {{cannot use property wrapper projection argument}}
187+
188+
func baz(@ProjectionWrapper x: Int, @ProjectionWrapper y: Int) {}
189+
baz($x:y:)(ProjectionWrapper(wrappedValue: 0), 0)
190+
baz(x:$y:)(0, ProjectionWrapper(wrappedValue: 0))
191+
192+
let _: (ProjectionWrapper<Int>, Int) -> Void = baz($x:y:)
193+
let _: (Int, ProjectionWrapper<Int>) -> Void = baz(x:$y:)
194+
}
195+
162196
func testImplicitPropertyWrapper() {
163197
typealias PropertyWrapperTuple = (ProjectionWrapper<Int>, Int, ProjectionWrapper<Int>)
164198

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct S {
2+
static func foo(_ x: Int) -> S {}
3+
static func foo(_ x: String) -> S {}
4+
}
5+
6+
// https://github.com/swiftlang/swift/issues/77981 - Make sure we can resolve
7+
// solver-based cursor info for UnresolvedMemberExprs.
8+
func bar() {
9+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):15 %s -- %s | %FileCheck %s
10+
let _: S = .foo()
11+
}
12+
13+
// CHECK-DAG: source.lang.swift.ref.function.method.static (2:15-2:28)
14+
// CHECK-DAG: source.lang.swift.ref.function.method.static (3:15-3:31)

0 commit comments

Comments
 (0)