Skip to content

Commit 179c81d

Browse files
committed
Use getFunctionArgApplyInfo in escaping diagnostics
This simplifies the implementation and fixes a compiler crasher. Resolves SR-10811.
1 parent a84cdbc commit 179c81d

File tree

4 files changed

+97
-61
lines changed

4 files changed

+97
-61
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -555,19 +555,8 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
555555
return false;
556556

557557
auto *anchor = getAnchor();
558-
auto *locator = getLocator();
559558
auto diagnostic = diag::general_noescape_to_escaping;
560559

561-
auto getGenericParamType =
562-
[](TypeVariableType *typeVar) -> GenericTypeParamType * {
563-
auto *locator = typeVar->getImpl().getLocator();
564-
if (locator->isForGenericParameter()) {
565-
const auto &GP = locator->getPath().back();
566-
return GP.getGenericParameter();
567-
}
568-
return nullptr;
569-
};
570-
571560
ParamDecl *PD = nullptr;
572561
if (auto *DRE = dyn_cast<DeclRefExpr>(anchor)) {
573562
PD = dyn_cast<ParamDecl>(DRE->getDecl());
@@ -580,37 +569,29 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
580569
// Let's check whether this is a function parameter passed
581570
// as an argument to another function which accepts @escaping
582571
// function at that position.
583-
auto path = locator->getPath();
584-
if (!path.empty() &&
585-
(path.back().getKind() == ConstraintLocator::ApplyArgToParam)) {
586-
if (auto paramType =
587-
getParameterTypeFor(getRawAnchor(), path.back().getValue2())) {
588-
if (paramType->isTypeVariableOrMember()) {
589-
auto diagnoseGenericParamFailure = [&](Type genericParam,
590-
GenericTypeParamDecl *decl) {
591-
emitDiagnostic(anchor->getLoc(),
592-
diag::converting_noespace_param_to_generic_type,
593-
PD->getName(), genericParam);
594-
595-
emitDiagnostic(decl, diag::generic_parameters_always_escaping);
596-
};
597-
598-
// If this is a situation when non-escaping parameter is passed
599-
// to the argument which represents generic parameter, there is
600-
// a tailored diagnostic for that.
601-
602-
if (auto *DMT = paramType->getAs<DependentMemberType>()) {
603-
auto baseTy = DMT->getBase()->castTo<TypeVariableType>();
604-
diagnoseGenericParamFailure(resolveType(DMT),
605-
getGenericParamType(baseTy)->getDecl());
606-
return true;
607-
}
572+
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) {
573+
auto paramInterfaceTy = argApplyInfo->getParamInterfaceType();
574+
if (paramInterfaceTy->isTypeParameter()) {
575+
auto diagnoseGenericParamFailure = [&](GenericTypeParamDecl *decl) {
576+
emitDiagnostic(anchor->getLoc(),
577+
diag::converting_noespace_param_to_generic_type,
578+
PD->getName(), paramInterfaceTy);
579+
580+
emitDiagnostic(decl, diag::generic_parameters_always_escaping);
581+
};
582+
583+
// If this is a situation when non-escaping parameter is passed
584+
// to the argument which represents generic parameter, there is
585+
// a tailored diagnostic for that.
586+
587+
if (auto *DMT = paramInterfaceTy->getAs<DependentMemberType>()) {
588+
diagnoseGenericParamFailure(DMT->getRootGenericParam()->getDecl());
589+
return true;
590+
}
608591

609-
auto *typeVar = paramType->getAs<TypeVariableType>();
610-
if (auto *GP = getGenericParamType(typeVar)) {
611-
diagnoseGenericParamFailure(GP, GP->getDecl());
612-
return true;
613-
}
592+
if (auto *GP = paramInterfaceTy->getAs<GenericTypeParamType>()) {
593+
diagnoseGenericParamFailure(GP->getDecl());
594+
return true;
614595
}
615596
}
616597

@@ -641,20 +622,6 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
641622
return true;
642623
}
643624

644-
Type NoEscapeFuncToTypeConversionFailure::getParameterTypeFor(
645-
Expr *expr, unsigned paramIdx) const {
646-
auto choice = getChoiceFor(expr);
647-
if (!choice)
648-
return Type();
649-
650-
if (auto *fnType = choice->openedType->getAs<FunctionType>()) {
651-
const auto &param = fnType->getParams()[paramIdx];
652-
return param.getPlainType();
653-
}
654-
655-
return Type();
656-
}
657-
658625
bool MissingForcedDowncastFailure::diagnoseAsError() {
659626
if (hasComplexLocator())
660627
return false;

lib/Sema/CSDiagnostics.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,10 +489,6 @@ class NoEscapeFuncToTypeConversionFailure final : public FailureDiagnostic {
489489
/// passing such parameter as an @escaping argument, or trying to
490490
/// assign it to a variable which expects @escaping function.
491491
bool diagnoseParameterUse() const;
492-
493-
/// Retrieve a type of the parameter at give index for call or
494-
/// subscript invocation represented by given expression node.
495-
Type getParameterTypeFor(Expr *expr, unsigned paramIdx) const;
496492
};
497493

498494
class MissingForcedDowncastFailure final : public FailureDiagnostic {

test/Constraints/function.swift

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,79 @@ func test_passing_noescape_function_to_dependent_member() {
129129

130130
func test(_ s: S<Q>, fn: () -> Int) {
131131
s.foo(fn)
132-
// expected-error@-1 {{converting non-escaping parameter 'fn' to generic parameter 'Q.U' (aka '() -> Int') may allow it to escape}}
132+
// expected-error@-1 {{converting non-escaping parameter 'fn' to generic parameter 'T.U' may allow it to escape}}
133133
}
134134
}
135+
136+
protocol Q {
137+
associatedtype U : P
138+
}
139+
140+
func sr10811(_ fn: () -> Int) {
141+
struct S1 : P {
142+
typealias U = () -> Int
143+
}
144+
145+
struct S2 : Q {
146+
typealias U = S1
147+
}
148+
149+
struct S<T : Q> { // expected-note {{generic parameters are always considered '@escaping'}}
150+
func foo(_ x: T.U.U) {}
151+
}
152+
153+
S<S2>().foo(fn) // expected-error {{converting non-escaping parameter 'fn' to generic parameter 'T.U.U' may allow it to escape}}
154+
}
155+
156+
struct Wrapper<U> {
157+
var value: U
158+
init(_ value: U) { self.value = value }
159+
}
160+
161+
func with<T>(_ x: T, body: (T) -> Void) {}
162+
func takesGeneric<T>(_ x: T) {}
163+
func takesEscapingFn(_ fn: @escaping () -> Int) {}
164+
func returnsTakesEscapingFn() -> (@escaping () -> Int) -> Void { takesEscapingFn }
165+
166+
prefix operator ^^^
167+
prefix func ^^^(_ x: Int) -> (@escaping () -> Int) -> Void { takesEscapingFn }
168+
169+
func testWeirdFnExprs<T>(_ fn: () -> Int, _ cond: Bool, _ any: Any, genericArg: T) { // expected-note 11{{parameter 'fn' is implicitly non-escaping}}
170+
(any as! (@escaping () -> Int) -> Void)(fn)
171+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
172+
173+
let wrapped = Wrapper<(@escaping () -> Int) -> Void>({ x in })
174+
(wrapped[keyPath: \.value] as (@escaping () -> Int) -> Void)(fn)
175+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
176+
177+
(cond ? returnsTakesEscapingFn() : returnsTakesEscapingFn())(fn)
178+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
179+
180+
(^^^5)(fn)
181+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
182+
183+
var optFn: Optional = takesEscapingFn
184+
optFn?(fn)
185+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
186+
187+
[takesEscapingFn][0](fn)
188+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
189+
190+
(takesEscapingFn, "").0(fn)
191+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
192+
193+
with({ (x: @escaping () -> Int) in }) { y in
194+
Wrapper(y).value(fn)
195+
// expected-error @-1{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
196+
}
197+
198+
_ = { x in (x({ 0 }), x(fn)) }(takesGeneric)
199+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
200+
201+
_ = { (a: (@escaping () -> Int), b) in () }(fn, genericArg)
202+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
203+
204+
func returnsVeryCurried() -> () throws -> (@escaping () -> Int) -> Void { { { x in } } }
205+
(try? returnsVeryCurried()())?(fn)
206+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
207+
}

test/attr/attr_autoclosure.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ func autoclosure_param_returning_func_type() {
255255

256256
func bar_1(_ fn: @autoclosure @escaping () -> Int) { foo(fn) } // Ok
257257
func bar_2(_ fn: @autoclosure () -> Int) { foo(fn) } // expected-note {{parameter 'fn' is implicitly non-escaping}}
258-
// expected-error@-1 {{using non-escaping parameter 'fn' in a context expecting an @escaping closure}}
258+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
259259
func baz_1(_ fn: @autoclosure @escaping () -> Int) { generic_foo(fn) } // Ok (T is inferred as () -> Int)
260260
func baz_2(_ fn: @autoclosure @escaping () -> Int) { generic_foo(fn()) } // Ok (T is inferred as Int)
261261
func baz_3(_ fn: @autoclosure () -> Int) { generic_foo(fn) } // Fails because fn is not marked as @escaping

0 commit comments

Comments
 (0)