Skip to content

Commit e14cc4c

Browse files
authored
Merge pull request #32248 from DougGregor/infer-function-builder-dynamic-replacement
2 parents 3531852 + 88589e1 commit e14cc4c

File tree

3 files changed

+147
-34
lines changed

3 files changed

+147
-34
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5075,8 +5075,8 @@ ERROR(function_builder_infer_ambig, none,
50755075
NOTE(function_builder_infer_add_return, none,
50765076
"add an explicit 'return' statement to not use a function builder", ())
50775077
NOTE(function_builder_infer_pick_specific, none,
5078-
"apply function builder %0 (inferred from protocol %1)",
5079-
(Type, DeclName))
5078+
"apply function builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)",
5079+
(Type, unsigned, DeclName))
50805080

50815081
//------------------------------------------------------------------------------
50825082
// MARK: Tuple Shuffle Diagnostics

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 97 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -210,45 +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 idc = cast<IterableDeclContext>(dc->getAsDecl());
218-
auto conformances = evaluateOrDefault(
219-
dc->getASTContext().evaluator,
220-
LookupAllConformancesInContextRequest{idc}, { });
221-
222213
// Find all of the potentially inferred function builder types.
223214
struct Match {
224-
ProtocolConformance *conformance;
225-
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+
226229
Type functionBuilderType;
227-
};
228-
SmallVector<Match, 2> matches;
229-
for (auto conformance : conformances) {
230-
auto protocol = conformance->getProtocol();
231-
for (auto found : protocol->lookupDirect(lookupDecl->getName())) {
232-
if (!isa<ProtocolDecl>(found->getDeclContext()))
233-
continue;
234230

235-
auto requirement = dyn_cast<ValueDecl>(found);
236-
if (!requirement)
237-
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+
}
238242

239-
Type functionBuilderType = requirement->getFunctionBuilderType();
240-
if (!functionBuilderType)
241-
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+
}
242251

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

247-
// Substitute into the function builder type.
248-
auto subs = conformance->getSubstitutions(decl->getModuleContext());
249-
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;
250265

251-
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{idc}, { });
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);
252316
}
253317
}
254318

@@ -274,7 +338,8 @@ static Type inferFunctionBuilderType(ValueDecl *decl) {
274338
decl->diagnose(
275339
diag::function_builder_infer_pick_specific,
276340
match.functionBuilderType,
277-
match.conformance->getProtocol()->getName())
341+
static_cast<unsigned>(match.kind),
342+
match.getSourceName())
278343
.fixItInsert(
279344
lookupDecl->getAttributeInsertionLoc(false),
280345
"@" + match.functionBuilderType.getString() + " ");

test/Constraints/function_builder_infer.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,51 @@ 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: OtherTupled {
227+
@_dynamicReplacement(for: dynamicTuple)
228+
var tuple: some Any { // expected-error{{ambiguous function builder inferred for 'tuple': 'OtherTupleBuilder' or 'TupleBuilder'}}
229+
// expected-note@-1{{add an explicit 'return' statement to not use a function builder}}
230+
// expected-note@-2{{apply function builder 'OtherTupleBuilder' (inferred from protocol 'OtherTupled')}}
231+
// expected-note@-3{{apply function builder 'TupleBuilder' (inferred from dynamic replacement of 'dynamicTuple')}}
232+
0
233+
}
234+
}

0 commit comments

Comments
 (0)