Skip to content

Commit e2634ab

Browse files
committed
[ConstraintSystem] Add a tailored diagnostic for conversion of non-escaping parameter to dependent member type
(cherry picked from commit d275c8a)
1 parent 8808fd8 commit e2634ab

File tree

3 files changed

+57
-23
lines changed

3 files changed

+57
-23
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3081,7 +3081,7 @@ ERROR(passing_noescape_to_escaping,none,
30813081
(Identifier))
30823082
ERROR(converting_noespace_param_to_generic_type,none,
30833083
"converting non-escaping parameter %0 to generic parameter %1 may allow it to escape",
3084-
(Identifier, Identifier))
3084+
(Identifier, Type))
30853085
ERROR(assigning_noescape_to_escaping,none,
30863086
"assigning non-escaping parameter %0 to an @escaping closure",
30873087
(Identifier))

lib/Sema/CSDiagnostics.cpp

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,16 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
459459
auto *locator = getLocator();
460460
auto diagnostic = diag::general_noescape_to_escaping;
461461

462+
auto getGenericParamType =
463+
[](TypeVariableType *typeVar) -> GenericTypeParamType * {
464+
auto *locator = typeVar->getImpl().getLocator();
465+
if (locator->isForGenericParameter()) {
466+
const auto &GP = locator->getPath().back();
467+
return GP.getGenericParameter();
468+
}
469+
return nullptr;
470+
};
471+
462472
ParamDecl *PD = nullptr;
463473
if (auto *DRE = dyn_cast<DeclRefExpr>(anchor)) {
464474
PD = dyn_cast<ParamDecl>(DRE->getDecl());
@@ -476,17 +486,32 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
476486
(path.back().getKind() == ConstraintLocator::ApplyArgToParam)) {
477487
if (auto paramType =
478488
getParameterTypeFor(getRawAnchor(), path.back().getValue2())) {
479-
// If this is a situation when non-escaping parameter is passed
480-
// to the argument which represents generic parameter, there is
481-
// a tailored diagnostic for that.
482-
if (auto *GP = paramType->getAs<GenericTypeParamType>()) {
483-
emitDiagnostic(anchor->getLoc(),
484-
diag::converting_noespace_param_to_generic_type,
485-
PD->getName(), GP->getName());
486-
487-
emitDiagnostic(GP->getDecl(),
488-
diag::generic_parameters_always_escaping);
489-
return true;
489+
if (paramType->isTypeVariableOrMember()) {
490+
auto diagnoseGenericParamFailure = [&](Type genericParam,
491+
GenericTypeParamDecl *decl) {
492+
emitDiagnostic(anchor->getLoc(),
493+
diag::converting_noespace_param_to_generic_type,
494+
PD->getName(), genericParam);
495+
496+
emitDiagnostic(decl, diag::generic_parameters_always_escaping);
497+
};
498+
499+
// If this is a situation when non-escaping parameter is passed
500+
// to the argument which represents generic parameter, there is
501+
// a tailored diagnostic for that.
502+
503+
if (auto *DMT = paramType->getAs<DependentMemberType>()) {
504+
auto baseTy = DMT->getBase()->castTo<TypeVariableType>();
505+
diagnoseGenericParamFailure(resolveType(DMT),
506+
getGenericParamType(baseTy)->getDecl());
507+
return true;
508+
}
509+
510+
auto *typeVar = paramType->getAs<TypeVariableType>();
511+
if (auto *GP = getGenericParamType(typeVar)) {
512+
diagnoseGenericParamFailure(GP, GP->getDecl());
513+
return true;
514+
}
490515
}
491516
}
492517

@@ -550,17 +575,7 @@ Type NoEscapeFuncToTypeConversionFailure::getParameterTypeFor(
550575

551576
if (auto *fnType = choice->openedType->getAs<FunctionType>()) {
552577
const auto &param = fnType->getParams()[paramIdx];
553-
554-
auto paramType = param.getPlainType();
555-
if (auto *typeVar = paramType->getAs<TypeVariableType>()) {
556-
auto *locator = typeVar->getImpl().getLocator();
557-
if (locator->isForGenericParameter()) {
558-
const auto &GP = locator->getPath().back();
559-
return GP.getGenericParameter();
560-
}
561-
}
562-
563-
return paramType;
578+
return param.getPlainType();
564579
}
565580

566581
return Type();

test/Constraints/function.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,22 @@ func foo(block: () -> (), other: () -> Int) {
106106
takesAny(block) // expected-error {{converting non-escaping value to 'Any' may allow it to escape}}
107107
takesAny(other) // expected-error {{converting non-escaping value to 'Any' may allow it to escape}}
108108
}
109+
110+
protocol P {
111+
associatedtype U
112+
}
113+
114+
func test_passing_noescape_function_to_dependent_member() {
115+
struct S<T : P> { // expected-note {{generic parameters are always considered '@escaping'}}
116+
func foo(_: T.U) {}
117+
}
118+
119+
struct Q : P {
120+
typealias U = () -> Int
121+
}
122+
123+
func test(_ s: S<Q>, fn: () -> Int) {
124+
s.foo(fn)
125+
// expected-error@-1 {{converting non-escaping parameter 'fn' to generic parameter 'Q.U' (aka '() -> Int') may allow it to escape}}
126+
}
127+
}

0 commit comments

Comments
 (0)