From 9a5f2dc98d4078f786ccb598627b98eadb9c1936 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 8 Jun 2020 13:22:47 -0700 Subject: [PATCH 1/2] [Function builders] Infer function builder through @_dynamicReplacement(for:). Fixes rdar://problem/63898120. --- include/swift/AST/DiagnosticsSema.def | 4 +- lib/Sema/TypeCheckRequestFunctions.cpp | 129 +++++++++++++----- test/Constraints/function_builder_infer.swift | 45 ++++++ 3 files changed, 144 insertions(+), 34 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e8581d3cbc515..26ab431c934fa 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5075,8 +5075,8 @@ ERROR(function_builder_infer_ambig, none, NOTE(function_builder_infer_add_return, none, "add an explicit 'return' statement to not use a function builder", ()) NOTE(function_builder_infer_pick_specific, none, - "apply function builder %0 (inferred from protocol %1)", - (Type, DeclName)) + "apply function builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)", + (Type, unsigned, DeclName)) //------------------------------------------------------------------------------ // MARK: Tuple Shuffle Diagnostics diff --git a/lib/Sema/TypeCheckRequestFunctions.cpp b/lib/Sema/TypeCheckRequestFunctions.cpp index bf26315290381..7ab8c6511f595 100644 --- a/lib/Sema/TypeCheckRequestFunctions.cpp +++ b/lib/Sema/TypeCheckRequestFunctions.cpp @@ -210,45 +210,109 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { lookupDecl = accessor->getStorage(); } - // Determine all of the conformances within the same context as - // this declaration. If this declaration is a witness to any - // requirement within one of those protocols that has a function builder - // attached, use that function builder type. - auto idc = cast(dc->getAsDecl()); - auto conformances = evaluateOrDefault( - dc->getASTContext().evaluator, - LookupAllConformancesInContextRequest{idc}, { }); - // Find all of the potentially inferred function builder types. struct Match { - ProtocolConformance *conformance; - ValueDecl *requirement; + enum Kind { + Conformance, + DynamicReplacement, + } kind; + + union { + struct { + ProtocolConformance *conformance; + ValueDecl *requirement; + } conformanceMatch; + + ValueDecl *dynamicReplacement; + }; + Type functionBuilderType; - }; - SmallVector matches; - for (auto conformance : conformances) { - auto protocol = conformance->getProtocol(); - for (auto found : protocol->lookupDirect(lookupDecl->getName())) { - if (!isa(found->getDeclContext())) - continue; - auto requirement = dyn_cast(found); - if (!requirement) - continue; + static Match forConformance( + ProtocolConformance *conformance, + ValueDecl *requirement, + Type functionBuilderType) { + Match match; + match.kind = Conformance; + match.conformanceMatch.conformance = conformance; + match.conformanceMatch.requirement = requirement; + match.functionBuilderType = functionBuilderType; + return match; + } - Type functionBuilderType = requirement->getFunctionBuilderType(); - if (!functionBuilderType) - continue; + static Match forDynamicReplacement( + ValueDecl *dynamicReplacement, Type functionBuilderType) { + Match match; + match.kind = DynamicReplacement; + match.dynamicReplacement = dynamicReplacement; + match.functionBuilderType = functionBuilderType; + return match; + } - auto witness = conformance->getWitnessDecl(requirement); - if (witness != lookupDecl) - continue; + DeclName getSourceName() const { + switch (kind) { + case Conformance: + return conformanceMatch.conformance->getProtocol()->getName(); - // Substitute into the function builder type. - auto subs = conformance->getSubstitutions(decl->getModuleContext()); - Type subFunctionBuilderType = functionBuilderType.subst(subs); + case DynamicReplacement: + return dynamicReplacement->getName(); + } + } + }; + + // The set of matches from which we can infer function builder types. + SmallVector matches; - matches.push_back({conformance, requirement, subFunctionBuilderType}); + // Determine all of the conformances within the same context as + // this declaration. If this declaration is a witness to any + // requirement within one of those protocols that has a function builder + // attached, use that function builder type. + auto addConformanceMatches = [&matches](ValueDecl *lookupDecl) { + DeclContext *dc = lookupDecl->getDeclContext(); + auto idc = cast(dc->getAsDecl()); + auto conformances = evaluateOrDefault( + dc->getASTContext().evaluator, + LookupAllConformancesInContextRequest{idc}, { }); + + for (auto conformance : conformances) { + auto protocol = conformance->getProtocol(); + for (auto found : protocol->lookupDirect(lookupDecl->getName())) { + if (!isa(found->getDeclContext())) + continue; + + auto requirement = dyn_cast(found); + if (!requirement) + continue; + + Type functionBuilderType = requirement->getFunctionBuilderType(); + if (!functionBuilderType) + continue; + + auto witness = conformance->getWitnessDecl(requirement); + if (witness != lookupDecl) + continue; + + // Substitute into the function builder type. + auto subs = + conformance->getSubstitutions(lookupDecl->getModuleContext()); + Type subFunctionBuilderType = functionBuilderType.subst(subs); + + matches.push_back( + Match::forConformance( + conformance, requirement, subFunctionBuilderType)); + } + } + }; + + addConformanceMatches(lookupDecl); + + // Look for function builder types inferred through dynamic replacements. + if (auto replaced = lookupDecl->getDynamicallyReplacedDecl()) { + if (auto functionBuilderType = replaced->getFunctionBuilderType()) { + matches.push_back( + Match::forDynamicReplacement(replaced, functionBuilderType)); + } else { + addConformanceMatches(replaced); } } @@ -274,7 +338,8 @@ static Type inferFunctionBuilderType(ValueDecl *decl) { decl->diagnose( diag::function_builder_infer_pick_specific, match.functionBuilderType, - match.conformance->getProtocol()->getName()) + static_cast(match.kind), + match.getSourceName()) .fixItInsert( lookupDecl->getAttributeInsertionLoc(false), "@" + match.functionBuilderType.getString() + " "); diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/function_builder_infer.swift index 7444cbb179110..9b4228f4ef8aa 100644 --- a/test/Constraints/function_builder_infer.swift +++ b/test/Constraints/function_builder_infer.swift @@ -184,3 +184,48 @@ struct TupleMeResolvedExplicit: Tupled, OtherTupled { return "hello" } } + +// Inference through dynamic replacement +struct DynamicTupled: Tupled { + dynamic var tuple: some Any { + return "hello" + } +} + +extension DynamicTupled { + @_dynamicReplacement(for: tuple) + var replacementTuple: some Any { + 1 + 3.14159 + "hello" + } +} + +struct DynamicTupled2: Tupled, OtherTupled { + dynamic var tuple: some Any { + return "hello" + } +} + +extension DynamicTupled2 { + @_dynamicReplacement(for: tuple) + var replacementTuple: some Any { // expected-error{{ambiguous function builder inferred for 'replacementTuple': 'TupleBuilder' or 'OtherTupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a function builder}} + // expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}} + // expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} + 1 + } +} + +struct DynamicTupled3 { + @TupleBuilder dynamic var dynamicTuple: some Any { + 0 + } +} + +extension DynamicTupled3: Tupled { + @_dynamicReplacement(for: dynamicTuple) + var tuple: some Any { + 0 + } +} From 88589e193013743b8d87ec7382ee4c99b568a971 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 8 Jun 2020 14:46:25 -0700 Subject: [PATCH 2/2] [Function builders] Improve test case for inference behavior --- test/Constraints/function_builder_infer.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/Constraints/function_builder_infer.swift b/test/Constraints/function_builder_infer.swift index 9b4228f4ef8aa..1715352d39424 100644 --- a/test/Constraints/function_builder_infer.swift +++ b/test/Constraints/function_builder_infer.swift @@ -223,9 +223,12 @@ struct DynamicTupled3 { } } -extension DynamicTupled3: Tupled { +extension DynamicTupled3: OtherTupled { @_dynamicReplacement(for: dynamicTuple) - var tuple: some Any { + var tuple: some Any { // expected-error{{ambiguous function builder inferred for 'tuple': 'OtherTupleBuilder' or 'TupleBuilder'}} + // expected-note@-1{{add an explicit 'return' statement to not use a function builder}} + // expected-note@-2{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}} + // expected-note@-3{{apply function builder 'TupleBuilder' (inferred from dynamic replacement of 'dynamicTuple')}} 0 } }