Skip to content

Commit 48792bb

Browse files
[Sema] Add tailored diagnostic for a escapeness mismatch in a fn type parameter position
1 parent c36a98e commit 48792bb

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3890,6 +3890,13 @@ ERROR(converting_noattrfunc_to_type,none,
38903890
"converting %select{non-escaping|non-concurrent function}0 value to %1 "
38913891
"may %select{allow it to escape|introduce data races}0",
38923892
(unsigned, Type))
3893+
ERROR(converting_noattrfunc_contravariant_parameter_position,none,
3894+
"converting escaping to non-escaping functions of type %2 in parameter position "
3895+
"#%0 of function type member %1 is not allowed",
3896+
(unsigned, Identifier, Type))
3897+
NOTE(add_explicit_escaping,none,
3898+
"add explicit @escaping to function parameter #%0",
3899+
(unsigned))
38933900

38943901
ERROR(converting_func_loses_global_actor,none,
38953902
"converting function value of type %0 to %1 loses global actor %2",

lib/Sema/CSDiagnostics.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,81 @@ bool AttributedFuncToTypeConversionFailure::diagnoseAsError() {
905905
return true;
906906
}
907907

908+
bool AttributedFuncToTypeConversionFailure::
909+
diagnoseFunctionParameterEscapenessMismatch(AssignExpr *AE) const {
910+
auto loc = getLocator();
911+
if (attributeKind != Escaping)
912+
return false;
913+
914+
if (!loc->findLast<LocatorPathElt::FunctionArgument>())
915+
return false;
916+
917+
auto *URDE = dyn_cast<UnresolvedDotExpr>(AE->getDest());
918+
if (!URDE)
919+
return false;
920+
921+
auto &solution = getSolution();
922+
auto memberLoc = getConstraintLocator(URDE, ConstraintLocator::Member);
923+
auto overload = solution.getOverloadChoiceIfAvailable(memberLoc);
924+
if (!overload)
925+
return false;
926+
927+
auto decl = dyn_cast_or_null<VarDecl>(overload->choice.getDecl());
928+
if (!decl)
929+
return false;
930+
931+
// The tuple locator element will give us the exact parameter mismatch
932+
// position.
933+
auto tupleElt = loc->getLastElementAs<LocatorPathElt::TupleElement>();
934+
auto mismatchPosition = tupleElt ? tupleElt->getIndex() : 0;
935+
936+
auto destInterfaceType =
937+
decl->getInterfaceType()->lookThroughAllOptionalTypes();
938+
auto destInterfaceFnType = destInterfaceType->castTo<FunctionType>();
939+
auto param = destInterfaceFnType->getParams()[mismatchPosition];
940+
emitDiagnostic(diag::converting_noattrfunc_contravariant_parameter_position,
941+
mismatchPosition, decl->getName(), param.getParameterType());
942+
943+
auto note = emitDiagnosticAt(decl->getLoc(), diag::add_explicit_escaping,
944+
mismatchPosition);
945+
946+
auto declRepr = decl->getTypeReprOrParentPatternTypeRepr();
947+
class TopLevelFuncReprFinder : public ASTWalker {
948+
bool walkToTypeReprPre(TypeRepr *TR) override {
949+
FnRepr = dyn_cast<FunctionTypeRepr>(TR);
950+
return FnRepr == nullptr;
951+
}
952+
953+
public:
954+
FunctionTypeRepr *FnRepr;
955+
TopLevelFuncReprFinder() : FnRepr(nullptr) {}
956+
};
957+
958+
// Look to find top-level function repr that maybe inside optional
959+
// representations.
960+
TopLevelFuncReprFinder fnFinder;
961+
declRepr->walk(fnFinder);
962+
963+
auto declFnRepr = fnFinder.FnRepr;
964+
if (!declFnRepr)
965+
return false;
966+
967+
auto argsRepr = declFnRepr->getArgsTypeRepr();
968+
auto argRepr = argsRepr->getElement(mismatchPosition).Type;
969+
if (!param.isAutoClosure()) {
970+
note.fixItInsert(argRepr->getStartLoc(), "@escaping ");
971+
} else {
972+
auto attrRepr = dyn_cast<AttributedTypeRepr>(argRepr);
973+
if (attrRepr) {
974+
auto autoclosureEndLoc = Lexer::getLocForEndOfToken(
975+
getASTContext().SourceMgr,
976+
attrRepr->getAttrs().getLoc(TAK_autoclosure));
977+
note.fixItInsertAfter(autoclosureEndLoc, " @escaping");
978+
}
979+
}
980+
return true;
981+
}
982+
908983
bool AttributedFuncToTypeConversionFailure::diagnoseParameterUse() const {
909984
auto convertTo = getToType();
910985
// If the other side is not a function, we have common case diagnostics
@@ -961,6 +1036,11 @@ bool AttributedFuncToTypeConversionFailure::diagnoseParameterUse() const {
9611036
diagnostic = diag::passing_noattrfunc_to_attrfunc;
9621037
}
9631038
} else if (auto *AE = getAsExpr<AssignExpr>(getRawAnchor())) {
1039+
// Attempt to diagnose escape/non-escape mismatch in function
1040+
// parameter position.
1041+
if (diagnoseFunctionParameterEscapenessMismatch(AE))
1042+
return true;
1043+
9641044
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getSrc())) {
9651045
PD = dyn_cast<ParamDecl>(DRE->getDecl());
9661046
diagnostic = diag::assigning_noattrfunc_to_attrfunc;

lib/Sema/CSDiagnostics.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,11 @@ class AttributedFuncToTypeConversionFailure final : public ContextualFailure {
740740
/// argument, or trying to assign it to a variable which expects @escaping
741741
/// or @Sendable function.
742742
bool diagnoseParameterUse() const;
743+
744+
/// Emit a tailored diagnostic for a no-escape/espace mismatch for function
745+
/// arguments where the mismatch has to take into account that a
746+
/// function type subtype relation in the parameter position is contravariant.
747+
bool diagnoseFunctionParameterEscapenessMismatch(AssignExpr *) const;
743748
};
744749

745750
/// Diagnose failure where a global actor attribute is dropped when

0 commit comments

Comments
 (0)