Skip to content

Commit e52ec93

Browse files
committed
[Function builders] Infer function builder through @_dynamicReplacement(for:).
Fixes rdar://problem/63898120.
1 parent 1ebb558 commit e52ec93

File tree

3 files changed

+144
-33
lines changed

3 files changed

+144
-33
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5054,8 +5054,8 @@ ERROR(function_builder_infer_ambig, none,
50545054
NOTE(function_builder_infer_add_return, none,
50555055
"add an explicit 'return' statement to not use a function builder", ())
50565056
NOTE(function_builder_infer_pick_specific, none,
5057-
"apply function builder %0 (inferred from protocol %1)",
5058-
(Type, DeclName))
5057+
"apply function builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)",
5058+
(Type, unsigned, DeclName))
50595059

50605060
//------------------------------------------------------------------------------
50615061
// MARK: Tuple Shuffle Diagnostics

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -210,44 +210,109 @@ static Type inferFunctionBuilderType(ValueDecl *decl) {
210210
lookupDecl = accessor->getStorage();
211211
}
212212

213-
// Determine all of the conformances within the same context as
214-
// this declaration. If this declaration is a witness to any
215-
// requirement within one of those protocols that has a function builder
216-
// attached, use that function builder type.
217-
auto conformances = evaluateOrDefault(
218-
dc->getASTContext().evaluator,
219-
LookupAllConformancesInContextRequest{dc}, { });
220-
221213
// Find all of the potentially inferred function builder types.
222214
struct Match {
223-
ProtocolConformance *conformance;
224-
ValueDecl *requirement;
215+
enum Kind {
216+
Conformance,
217+
DynamicReplacement,
218+
} kind;
219+
220+
union {
221+
struct {
222+
ProtocolConformance *conformance;
223+
ValueDecl *requirement;
224+
} conformanceMatch;
225+
226+
ValueDecl *dynamicReplacement;
227+
};
228+
225229
Type functionBuilderType;
226-
};
227-
SmallVector<Match, 2> matches;
228-
for (auto conformance : conformances) {
229-
auto protocol = conformance->getProtocol();
230-
for (auto found : protocol->lookupDirect(lookupDecl->getName())) {
231-
if (!isa<ProtocolDecl>(found->getDeclContext()))
232-
continue;
233230

234-
auto requirement = dyn_cast<ValueDecl>(found);
235-
if (!requirement)
236-
continue;
231+
static Match forConformance(
232+
ProtocolConformance *conformance,
233+
ValueDecl *requirement,
234+
Type functionBuilderType) {
235+
Match match;
236+
match.kind = Conformance;
237+
match.conformanceMatch.conformance = conformance;
238+
match.conformanceMatch.requirement = requirement;
239+
match.functionBuilderType = functionBuilderType;
240+
return match;
241+
}
237242

238-
Type functionBuilderType = requirement->getFunctionBuilderType();
239-
if (!functionBuilderType)
240-
continue;
243+
static Match forDynamicReplacement(
244+
ValueDecl *dynamicReplacement, Type functionBuilderType) {
245+
Match match;
246+
match.kind = DynamicReplacement;
247+
match.dynamicReplacement = dynamicReplacement;
248+
match.functionBuilderType = functionBuilderType;
249+
return match;
250+
}
241251

242-
auto witness = conformance->getWitnessDecl(requirement);
243-
if (witness != lookupDecl)
244-
continue;
252+
DeclName getSourceName() const {
253+
switch (kind) {
254+
case Conformance:
255+
return conformanceMatch.conformance->getProtocol()->getName();
245256

246-
// Substitute into the function builder type.
247-
auto subs = conformance->getSubstitutions(decl->getModuleContext());
248-
Type subFunctionBuilderType = functionBuilderType.subst(subs);
257+
case DynamicReplacement:
258+
return dynamicReplacement->getName();
259+
}
260+
}
261+
};
262+
263+
// The set of matches from which we can infer function builder types.
264+
SmallVector<Match, 2> matches;
249265

250-
matches.push_back({conformance, requirement, subFunctionBuilderType});
266+
// Determine all of the conformances within the same context as
267+
// this declaration. If this declaration is a witness to any
268+
// requirement within one of those protocols that has a function builder
269+
// attached, use that function builder type.
270+
auto addConformanceMatches = [&matches](ValueDecl *lookupDecl) {
271+
DeclContext *dc = lookupDecl->getDeclContext();
272+
auto idc = cast<IterableDeclContext>(dc->getAsDecl());
273+
auto conformances = evaluateOrDefault(
274+
dc->getASTContext().evaluator,
275+
LookupAllConformancesInContextRequest{dc}, { });
276+
277+
for (auto conformance : conformances) {
278+
auto protocol = conformance->getProtocol();
279+
for (auto found : protocol->lookupDirect(lookupDecl->getName())) {
280+
if (!isa<ProtocolDecl>(found->getDeclContext()))
281+
continue;
282+
283+
auto requirement = dyn_cast<ValueDecl>(found);
284+
if (!requirement)
285+
continue;
286+
287+
Type functionBuilderType = requirement->getFunctionBuilderType();
288+
if (!functionBuilderType)
289+
continue;
290+
291+
auto witness = conformance->getWitnessDecl(requirement);
292+
if (witness != lookupDecl)
293+
continue;
294+
295+
// Substitute into the function builder type.
296+
auto subs =
297+
conformance->getSubstitutions(lookupDecl->getModuleContext());
298+
Type subFunctionBuilderType = functionBuilderType.subst(subs);
299+
300+
matches.push_back(
301+
Match::forConformance(
302+
conformance, requirement, subFunctionBuilderType));
303+
}
304+
}
305+
};
306+
307+
addConformanceMatches(lookupDecl);
308+
309+
// Look for function builder types inferred through dynamic replacements.
310+
if (auto replaced = lookupDecl->getDynamicallyReplacedDecl()) {
311+
if (auto functionBuilderType = replaced->getFunctionBuilderType()) {
312+
matches.push_back(
313+
Match::forDynamicReplacement(replaced, functionBuilderType));
314+
} else {
315+
addConformanceMatches(replaced);
251316
}
252317
}
253318

@@ -273,7 +338,8 @@ static Type inferFunctionBuilderType(ValueDecl *decl) {
273338
decl->diagnose(
274339
diag::function_builder_infer_pick_specific,
275340
match.functionBuilderType,
276-
match.conformance->getProtocol()->getName())
341+
static_cast<unsigned>(match.kind),
342+
match.getSourceName())
277343
.fixItInsert(
278344
lookupDecl->getAttributeInsertionLoc(false),
279345
"@" + match.functionBuilderType.getString() + " ");

test/Constraints/function_builder_infer.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,48 @@ struct TupleMeResolvedExplicit: Tupled, OtherTupled {
184184
return "hello"
185185
}
186186
}
187+
188+
// Inference through dynamic replacement
189+
struct DynamicTupled: Tupled {
190+
dynamic var tuple: some Any {
191+
return "hello"
192+
}
193+
}
194+
195+
extension DynamicTupled {
196+
@_dynamicReplacement(for: tuple)
197+
var replacementTuple: some Any {
198+
1
199+
3.14159
200+
"hello"
201+
}
202+
}
203+
204+
struct DynamicTupled2: Tupled, OtherTupled {
205+
dynamic var tuple: some Any {
206+
return "hello"
207+
}
208+
}
209+
210+
extension DynamicTupled2 {
211+
@_dynamicReplacement(for: tuple)
212+
var replacementTuple: some Any { // expected-error{{ambiguous function builder inferred for 'replacementTuple': 'TupleBuilder' or 'OtherTupleBuilder'}}
213+
// expected-note@-1{{add an explicit 'return' statement to not use a function builder}}
214+
// expected-note@-2{{apply function builder 'TupleBuilder' (inferred from protocol 'Tupled')}}
215+
// expected-note@-3{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}
216+
1
217+
}
218+
}
219+
220+
struct DynamicTupled3 {
221+
@TupleBuilder dynamic var dynamicTuple: some Any {
222+
0
223+
}
224+
}
225+
226+
extension DynamicTupled3: Tupled {
227+
@_dynamicReplacement(for: dynamicTuple)
228+
var tuple: some Any {
229+
0
230+
}
231+
}

0 commit comments

Comments
 (0)