Skip to content

Commit 07e5d54

Browse files
committed
[CSDiagnostics] Add custom diagnostic for invalid @autoclosure forwarding
Suggest to add `()` (form a call) to correctly forward argument function originated from `@autoclosure` parameter to function parameter itself marked as `@autoclosure`.
1 parent cc780e3 commit 07e5d54

File tree

6 files changed

+83
-7
lines changed

6 files changed

+83
-7
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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,9 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
204204
}
205205

206206
bool AutoClosureForwarding::diagnose(Expr *root, bool asNote) const {
207-
return false;
207+
auto failure =
208+
AutoClosureForwardingFailure(getConstraintSystem(), getLocator());
209+
return failure.diagnose(asNote);
208210
}
209211

210212
AutoClosureForwarding *AutoClosureForwarding::create(ConstraintSystem &cs,

test/Compatibility/attr_autoclosure.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,28 @@ do {
3737
}
3838
}
3939

40-
func passAutoClosureToSubscript(_ fn: @autoclosure () -> Int) {
40+
func passAutoClosureToSubscriptAndMember(_ fn: @autoclosure () -> Int) {
4141
struct S {
42+
func bar(_: Int, _ fun: @autoclosure () -> Int) {}
43+
4244
subscript(_ fn: @autoclosure () -> Int) -> Int { return fn() }
45+
46+
static func foo(_: @autoclosure () -> Int) {}
4347
}
4448

4549
let s = S()
50+
let _ = s.bar(42, fn) // Ok
4651
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
4764
}

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)