Skip to content

Commit 783f034

Browse files
authored
Merge pull request #20693 from xedin/forward-autoclosure-diagnostic
[CSDiagnostics] Add custom diagnostic for invalid @autoclosure forwarding
2 parents e1e22ed + 07e5d54 commit 783f034

File tree

10 files changed

+166
-22
lines changed

10 files changed

+166
-22
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,8 @@ ERROR(c_function_pointer_from_function_with_context,none,
10811081
"%select{local function|closure}0 that captures "
10821082
"%select{context|generic parameters|dynamic Self type}1",
10831083
(bool, unsigned))
1084+
ERROR(invalid_autoclosure_forwarding,none,
1085+
"add () to forward @autoclosure parameter", ())
10841086

10851087
//------------------------------------------------------------------------------
10861088
// MARK: Type Check Declarations

lib/Sema/CSDiagnostics.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,3 +1206,19 @@ bool ContextualFailure::trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
12061206

12071207
return false;
12081208
}
1209+
1210+
bool AutoClosureForwardingFailure::diagnoseAsError() {
1211+
auto path = getLocator()->getPath();
1212+
assert(!path.empty());
1213+
1214+
auto &last = path.back();
1215+
assert(last.getKind() == ConstraintLocator::ApplyArgToParam);
1216+
1217+
// We need a raw anchor here because `getAnchor()` is simplified
1218+
// to the argument expression.
1219+
auto *argExpr = getArgumentExpr(getRawAnchor(), last.getValue());
1220+
emitDiagnostic(argExpr->getLoc(), diag::invalid_autoclosure_forwarding)
1221+
.highlight(argExpr->getSourceRange())
1222+
.fixItInsertAfter(argExpr->getEndLoc(), "()");
1223+
return true;
1224+
}

lib/Sema/CSDiagnostics.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class FailureDiagnostic {
3939
ConstraintSystem &CS;
4040
ConstraintLocator *Locator;
4141

42+
/// The original anchor before any simplification.
43+
Expr *RawAnchor;
44+
/// Simplified anchor associated with the given locator.
4245
Expr *Anchor;
4346
/// Indicates whether locator could be simplified
4447
/// down to anchor expression.
@@ -47,7 +50,7 @@ class FailureDiagnostic {
4750
public:
4851
FailureDiagnostic(Expr *expr, ConstraintSystem &cs,
4952
ConstraintLocator *locator)
50-
: E(expr), CS(cs), Locator(locator) {
53+
: E(expr), CS(cs), Locator(locator), RawAnchor(locator->getAnchor()) {
5154
std::tie(Anchor, HasComplexLocator) = computeAnchor();
5255
}
5356

@@ -78,6 +81,8 @@ class FailureDiagnostic {
7881

7982
Expr *getParentExpr() const { return E; }
8083

84+
Expr *getRawAnchor() const { return RawAnchor; }
85+
8186
Expr *getAnchor() const { return Anchor; }
8287

8388
ConstraintLocator *getLocator() const { return Locator; }
@@ -592,6 +597,16 @@ class ContextualFailure final : public FailureDiagnostic {
592597
}
593598
};
594599

600+
/// Diagnose situations when @autoclosure argument is passed to @autoclosure
601+
/// parameter directly without calling it first.
602+
class AutoClosureForwardingFailure final : public FailureDiagnostic {
603+
public:
604+
AutoClosureForwardingFailure(ConstraintSystem &cs, ConstraintLocator *locator)
605+
: FailureDiagnostic(nullptr, cs, locator) {}
606+
607+
bool diagnoseAsError() override;
608+
};
609+
595610
} // end namespace constraints
596611
} // end namespace swift
597612

lib/Sema/CSFix.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,14 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
202202
ConstraintLocator *locator) {
203203
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
204204
}
205+
206+
bool AutoClosureForwarding::diagnose(Expr *root, bool asNote) const {
207+
auto failure =
208+
AutoClosureForwardingFailure(getConstraintSystem(), getLocator());
209+
return failure.diagnose(asNote);
210+
}
211+
212+
AutoClosureForwarding *AutoClosureForwarding::create(ConstraintSystem &cs,
213+
ConstraintLocator *locator) {
214+
return new (cs.getAllocator()) AutoClosureForwarding(cs, locator);
215+
}

lib/Sema/CSFix.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ enum class FixKind : uint8_t {
8383
/// Fix up one of the sides of conversion to make it seem
8484
/// like the types are aligned.
8585
ContextualMismatch,
86+
87+
/// Fix up @autoclosure argument to the @autoclosure parameter,
88+
/// to for a call to be able to foward it properly, since
89+
/// @autoclosure conversions are unsupported starting from
90+
/// Swift version 5.
91+
AutoClosureForwarding,
8692
};
8793

8894
class ConstraintFix {
@@ -379,6 +385,29 @@ class ContextualMismatch : public ConstraintFix {
379385
ConstraintLocator *locator);
380386
};
381387

388+
/// Detect situations when argument of the @autoclosure parameter is itself
389+
/// marked as @autoclosure and is not applied. Form a fix which suggests a
390+
/// proper way to forward such arguments, e.g.:
391+
///
392+
/// ```swift
393+
/// func foo(_ fn: @autoclosure () -> Int) {}
394+
/// func bar(_ fn: @autoclosure () -> Int) {
395+
/// foo(fn) // error - fn should be called
396+
/// }
397+
/// ```
398+
class AutoClosureForwarding final : public ConstraintFix {
399+
public:
400+
AutoClosureForwarding(ConstraintSystem &cs, ConstraintLocator *locator)
401+
: ConstraintFix(cs, FixKind::AutoClosureForwarding, locator) {}
402+
403+
std::string getName() const override { return "fix @autoclosure forwarding"; }
404+
405+
bool diagnose(Expr *root, bool asNote = false) const override;
406+
407+
static AutoClosureForwarding *create(ConstraintSystem &cs,
408+
ConstraintLocator *locator);
409+
};
410+
382411
} // end namespace constraints
383412
} // end namespace swift
384413

lib/Sema/CSSimplify.cpp

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -848,18 +848,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
848848
auto isAutoClosureArg = [&](Expr *anchor, unsigned argIdx) -> bool {
849849
assert(anchor);
850850

851-
auto *call = dyn_cast<ApplyExpr>(anchor);
852-
if (!call)
853-
return false;
854-
855-
Expr *argExpr = nullptr;
856-
if (auto *PE = dyn_cast<ParenExpr>(call->getArg())) {
857-
assert(argsWithLabels.size() == 1);
858-
argExpr = PE->getSubExpr();
859-
} else if (auto *TE = dyn_cast<TupleExpr>(call->getArg())) {
860-
argExpr = TE->getElement(argIdx);
861-
}
862-
851+
auto *argExpr = getArgumentExpr(anchor, argIdx);
863852
if (!argExpr)
864853
return false;
865854

@@ -895,11 +884,15 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
895884
// is itself `@autoclosure` function type in Swift < 5,
896885
// let's fix that up by making it look like argument is
897886
// called implicitly.
898-
if (!cs.getASTContext().isSwiftVersionAtLeast(5)) {
899-
if (param.isAutoClosure() &&
900-
isAutoClosureArg(locator.getAnchor(), argIdx)) {
901-
argTy = argTy->castTo<FunctionType>()->getResult();
902-
cs.increaseScore(SK_FunctionConversion);
887+
if (param.isAutoClosure() &&
888+
isAutoClosureArg(locator.getAnchor(), argIdx)) {
889+
argTy = argTy->castTo<FunctionType>()->getResult();
890+
cs.increaseScore(SK_FunctionConversion);
891+
892+
if (cs.getASTContext().isSwiftVersionAtLeast(5)) {
893+
auto *fixLoc = cs.getConstraintLocator(loc);
894+
if (cs.recordFix(AutoClosureForwarding::create(cs, fixLoc)))
895+
return cs.getTypeMatchFailure(loc);
903896
}
904897
}
905898

@@ -5431,6 +5424,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
54315424
case FixKind::CoerceToCheckedCast:
54325425
case FixKind::RelabelArguments:
54335426
case FixKind::AddConformance:
5427+
case FixKind::AutoClosureForwarding:
54345428
llvm_unreachable("handled elsewhere");
54355429
}
54365430

lib/Sema/ConstraintSystem.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,3 +2356,23 @@ Expr *constraints::simplifyLocatorToAnchor(ConstraintSystem &cs,
23562356

23572357
return locator->getAnchor();
23582358
}
2359+
2360+
Expr *constraints::getArgumentExpr(Expr *expr, unsigned index) {
2361+
Expr *argExpr = nullptr;
2362+
if (auto *AE = dyn_cast<ApplyExpr>(expr))
2363+
argExpr = AE->getArg();
2364+
else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(expr))
2365+
argExpr = UME->getArgument();
2366+
else if (auto *SE = dyn_cast<SubscriptExpr>(expr))
2367+
argExpr = SE->getIndex();
2368+
else
2369+
return nullptr;
2370+
2371+
if (auto *PE = dyn_cast<ParenExpr>(argExpr)) {
2372+
assert(index == 0);
2373+
return PE->getSubExpr();
2374+
}
2375+
2376+
assert(isa<TupleExpr>(argExpr));
2377+
return cast<TupleExpr>(argExpr)->getElement(index);
2378+
}

lib/Sema/ConstraintSystem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3466,6 +3466,13 @@ void simplifyLocator(Expr *&anchor,
34663466
/// null otherwise.
34673467
Expr *simplifyLocatorToAnchor(ConstraintSystem &cs, ConstraintLocator *locator);
34683468

3469+
/// Retrieve argument at specified index from given expression.
3470+
/// The expression could be "application", "subscript" or "member" call.
3471+
///
3472+
/// \returns argument expression or `nullptr` if given "base" expression
3473+
/// wasn't of one of the kinds listed above.
3474+
Expr *getArgumentExpr(Expr *expr, unsigned index);
3475+
34693476
class DisjunctionChoice {
34703477
unsigned Index;
34713478
Constraint *Choice;

test/Compatibility/attr_autoclosure.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,29 @@ do {
3636
foo(c)
3737
}
3838
}
39+
40+
func passAutoClosureToSubscriptAndMember(_ fn: @autoclosure () -> Int) {
41+
struct S {
42+
func bar(_: Int, _ fun: @autoclosure () -> Int) {}
43+
44+
subscript(_ fn: @autoclosure () -> Int) -> Int { return fn() }
45+
46+
static func foo(_: @autoclosure () -> Int) {}
47+
}
48+
49+
let s = S()
50+
let _ = s.bar(42, fn) // Ok
51+
let _ = s[fn] // Ok
52+
let _ = S.foo(fn) // Ok
53+
}
54+
55+
func passAutoClosureToEnumCase(_ fn: @escaping @autoclosure () -> Int) {
56+
enum E {
57+
case baz(@autoclosure () -> Int)
58+
}
59+
60+
let _: E = .baz(42) // Ok
61+
// FIXME: This line type-checks correctly but causes a crash
62+
// somewhere SILGen if `fn` doesn't have `@escaping`.
63+
let _: E = .baz(fn) // Ok
64+
}

test/attr/attr_autoclosure.swift

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
22

33
// Simple case.
44
var fn : @autoclosure () -> Int = 4 // expected-error {{'@autoclosure' may only be used on parameters}} expected-error {{cannot convert value of type 'Int' to specified type '() -> Int'}}
@@ -167,13 +167,37 @@ func variadicAutoclosure(_ fn: @autoclosure () -> ()...) {
167167
// These are all arguably invalid; the autoclosure should have to be called.
168168
// But as long as we allow them, we shouldn't crash.
169169
func passNonThrowingToNonThrowingAC(_ fn: @autoclosure () -> Int) {
170-
takesAutoclosure(fn)
170+
takesAutoclosure(fn) // expected-error {{add () to forward @autoclosure parameter}} {{22-22=()}}
171171
}
172172
func passNonThrowingToThrowingAC(_ fn: @autoclosure () -> Int) {
173-
takesThrowingAutoclosure(fn)
173+
takesThrowingAutoclosure(fn) // expected-error {{add () to forward @autoclosure parameter}} {{30-30=()}}
174174
}
175175
func passThrowingToThrowingAC(_ fn: @autoclosure () throws -> Int) {
176-
takesThrowingAutoclosure(fn)
176+
takesThrowingAutoclosure(fn) // expected-error {{add () to forward @autoclosure parameter}} {{30-30=()}}
177+
}
178+
179+
func passAutoClosureToSubscriptAndMember(_ fn: @autoclosure () -> Int) {
180+
struct S {
181+
func bar(_: Int, _ fun: @autoclosure () -> Int) {}
182+
183+
subscript(_ fn: @autoclosure () -> Int) -> Int { return fn() }
184+
185+
static func foo(_ fn: @autoclosure () -> Int) {}
186+
}
187+
188+
let s = S()
189+
let _ = s.bar(42, fn) // expected-error {{add () to forward @autoclosure parameter}} {{23-23=()}}
190+
let _ = s[fn] // expected-error {{add () to forward @autoclosure parameter}} {{15-15=()}}
191+
let _ = S.foo(fn) // expected-error {{add () to forward @autoclosure parameter}} {{19-19=()}}
192+
}
193+
194+
func passAutoClosureToEnumCase(_ fn: @autoclosure () -> Int) {
195+
enum E {
196+
case baz(@autoclosure () -> Int)
197+
}
198+
199+
let _: E = .baz(42) // Ok
200+
let _: E = .baz(fn) // expected-error {{add () to forward @autoclosure parameter}} {{21-21=()}}
177201
}
178202

179203
// rdar://problem/20591571 - Various type inference problems with @autoclosure

0 commit comments

Comments
 (0)