Skip to content

Commit d275c8a

Browse files
committed
[ConstraintSystem] Add a tailored diagnostic for conversion of non-escaping parameter to dependent member type
1 parent 62b2da8 commit d275c8a

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
@@ -463,6 +463,16 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
463463
auto *locator = getLocator();
464464
auto diagnostic = diag::general_noescape_to_escaping;
465465

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

@@ -554,17 +579,7 @@ Type NoEscapeFuncToTypeConversionFailure::getParameterTypeFor(
554579

555580
if (auto *fnType = choice->openedType->getAs<FunctionType>()) {
556581
const auto &param = fnType->getParams()[paramIdx];
557-
558-
auto paramType = param.getPlainType();
559-
if (auto *typeVar = paramType->getAs<TypeVariableType>()) {
560-
auto *locator = typeVar->getImpl().getLocator();
561-
if (locator->isForGenericParameter()) {
562-
const auto &GP = locator->getPath().back();
563-
return GP.getGenericParameter();
564-
}
565-
}
566-
567-
return paramType;
582+
return param.getPlainType();
568583
}
569584

570585
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)