Skip to content

Commit bf7eea0

Browse files
committed
[Function builders] Infer function builder from a protocol requirement.
Allow a protocol requirement for a function or property to declare a function builder. A witness to such a protocol requirement will infer that function builder when all of the following are two: * The witness does not explicitly adopt a function builder * The witness is declared within the same context that conforms to the protocol requirement (e.g., same extension or primary type declaration) * The witness's body does not contain a "return" statement (since those disable the function builder transform).
1 parent 2d25850 commit bf7eea0

File tree

9 files changed

+313
-19
lines changed

9 files changed

+313
-19
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5066,6 +5066,14 @@ NOTE(function_builder_remove_attr, none,
50665066
"remove the attribute to explicitly disable the function builder", ())
50675067
NOTE(function_builder_remove_returns, none,
50685068
"remove 'return' statements to apply the function builder", ())
5069+
ERROR(function_builder_infer_ambig, none,
5070+
"ambiguous function builder inferred for %0: %1 or %2",
5071+
(DeclName, Type, Type))
5072+
NOTE(function_builder_infer_add_return, none,
5073+
"add an explicit 'return' statement to not use a function builder", ())
5074+
NOTE(function_builder_infer_pick_specific, none,
5075+
"apply function builder %0 (inferred from protocol %1)",
5076+
(Type, DeclName))
50695077

50705078
//------------------------------------------------------------------------------
50715079
// MARK: Tuple Shuffle Diagnostics

lib/AST/Attr.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -758,11 +758,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
758758
case DAK_Custom: {
759759
if (!Options.IsForSwiftInterface)
760760
break;
761-
// For Swift interface, we should only print function builder attribute
762-
// on parameter decls. Printing the attribute elsewhere isn't ABI relevant.
761+
// For Swift interface, we should print function builder attributes
762+
// on parameter decls and on protocol requirements.
763+
// Printing the attribute elsewhere isn't ABI relevant.
763764
if (auto *VD = dyn_cast<ValueDecl>(D)) {
764765
if (VD->getAttachedFunctionBuilder() == this) {
765-
if (!isa<ParamDecl>(D))
766+
if (!isa<ParamDecl>(D) &&
767+
!(isa<VarDecl>(D) && isa<ProtocolDecl>(D->getDeclContext())))
766768
return false;
767769
}
768770
}

lib/AST/Decl.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6366,10 +6366,6 @@ void ParamDecl::setStoredProperty(VarDecl *var) {
63666366
}
63676367

63686368
Type ValueDecl::getFunctionBuilderType() const {
6369-
// Fast path: most declarations (especially parameters, which is where
6370-
// this is hottest) do not have any custom attributes at all.
6371-
if (!getAttrs().hasAttribute<CustomAttr>()) return Type();
6372-
63736369
auto &ctx = getASTContext();
63746370
auto mutableThis = const_cast<ValueDecl *>(this);
63756371
return evaluateOrDefault(ctx.evaluator,
@@ -6378,10 +6374,6 @@ Type ValueDecl::getFunctionBuilderType() const {
63786374
}
63796375

63806376
CustomAttr *ValueDecl::getAttachedFunctionBuilder() const {
6381-
// Fast path: most declarations (especially parameters, which is where
6382-
// this is hottest) do not have any custom attributes at all.
6383-
if (!getAttrs().hasAttribute<CustomAttr>()) return nullptr;
6384-
63856377
auto &ctx = getASTContext();
63866378
auto mutableThis = const_cast<ValueDecl *>(this);
63876379
return evaluateOrDefault(ctx.evaluator,

lib/Sema/BuilderTransform.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,10 +1401,6 @@ BraceStmt *swift::applyFunctionBuilderTransform(
14011401
captured.first, captured.second)));
14021402
}
14031403

1404-
/// Find the return statements in the given body, which block the application
1405-
/// of a function builder.
1406-
static std::vector<ReturnStmt *> findReturnStatements(AnyFunctionRef fn);
1407-
14081404
Optional<BraceStmt *> TypeChecker::applyFunctionBuilderBodyTransform(
14091405
FuncDecl *func, Type builderType) {
14101406
// Pre-check the body: pre-check any expressions in it and look
@@ -1708,7 +1704,7 @@ PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval,
17081704
return PreCheckFunctionBuilderApplication(fn, false).run();
17091705
}
17101706

1711-
std::vector<ReturnStmt *> findReturnStatements(AnyFunctionRef fn) {
1707+
std::vector<ReturnStmt *> TypeChecker::findReturnStatements(AnyFunctionRef fn) {
17121708
PreCheckFunctionBuilderApplication precheck(fn, true);
17131709
(void)precheck.run();
17141710
return precheck.getReturnStmts();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2971,9 +2971,11 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
29712971

29722972
// Module interfaces don't print bodies for all getters, so allow getters
29732973
// that don't have a body if we're compiling a module interface.
2974+
// Within a protocol definition, there will never be a body.
29742975
SourceFile *parent = storage->getDeclContext()->getParentSourceFile();
29752976
bool isInInterface = parent && parent->Kind == SourceFileKind::Interface;
2976-
if (!isInInterface && !getter->hasBody())
2977+
if (!isInInterface && !getter->hasBody() &&
2978+
!isa<ProtocolDecl>(storage->getDeclContext()))
29772979
return true;
29782980

29792981
return false;

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/AST/ExistentialLayout.h"
1919
#include "swift/AST/GenericSignature.h"
2020
#include "swift/AST/NameLookupRequests.h"
21+
#include "swift/AST/ProtocolConformance.h"
2122
#include "swift/AST/TypeLoc.h"
2223
#include "swift/AST/Types.h"
2324
#include "swift/Subsystems.h"
@@ -182,11 +183,114 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator,
182183
return nullptr;
183184
}
184185

186+
/// Attempt to infer the function builder type for a declaration.
187+
static Type inferFunctionBuilderType(ValueDecl *decl) {
188+
auto dc = decl->getDeclContext();
189+
if (!dc->isTypeContext())
190+
return Type();
191+
192+
auto funcDecl = dyn_cast<FuncDecl>(decl);
193+
if (!funcDecl)
194+
return Type();
195+
196+
// Only getters can have function builders. When we find one, look at
197+
// the storage declaration for the purposes of witness matching.
198+
auto lookupDecl = decl;
199+
if (auto accessor = dyn_cast<AccessorDecl>(funcDecl)) {
200+
if (accessor->getAccessorKind() != AccessorKind::Get)
201+
return Type();
202+
203+
lookupDecl = accessor->getStorage();
204+
}
205+
206+
// Determine all of the conformances within the same context as
207+
// this declaration. If this declaration is a witness to any
208+
// requirement within one of those protocols that has a function builder
209+
// attached, use that function builder type.
210+
auto idc = cast<IterableDeclContext>(dc->getAsDecl());
211+
auto conformances = evaluateOrDefault(
212+
dc->getASTContext().evaluator,
213+
LookupAllConformancesInContextRequest{idc}, { });
214+
215+
// Find all of the potentially inferred function builder types.
216+
struct Match {
217+
ProtocolConformance *conformance;
218+
ValueDecl *requirement;
219+
Type functionBuilderType;
220+
};
221+
SmallVector<Match, 2> matches;
222+
for (auto conformance : conformances) {
223+
auto protocol = conformance->getProtocol();
224+
for (auto found : protocol->lookupDirect(lookupDecl->getName())) {
225+
if (!isa<ProtocolDecl>(found->getDeclContext()))
226+
continue;
227+
228+
auto requirement = dyn_cast<ValueDecl>(found);
229+
if (!requirement)
230+
continue;
231+
232+
Type functionBuilderType = requirement->getFunctionBuilderType();
233+
if (!functionBuilderType)
234+
continue;
235+
236+
auto witness = conformance->getWitnessDecl(requirement);
237+
if (witness != lookupDecl)
238+
continue;
239+
240+
// Substitute into the function builder type.
241+
auto subs = conformance->getSubstitutions(decl->getModuleContext());
242+
Type subFunctionBuilderType = functionBuilderType.subst(subs);
243+
244+
matches.push_back({conformance, requirement, subFunctionBuilderType});
245+
}
246+
}
247+
248+
if (matches.size() == 0)
249+
return Type();
250+
251+
// Check whether there are any return statements in the function's body.
252+
// If there are, the function builder transform will be disabled,
253+
// so don't infer a function builder.
254+
if (!TypeChecker::findReturnStatements(funcDecl).empty())
255+
return Type();
256+
257+
// Determine whether there is more than one actual function builder type.
258+
Type functionBuilderType = matches[0].functionBuilderType;
259+
for (const auto &match : matches) {
260+
// If the types were the same anyway, there's nothing to do.
261+
Type otherFunctionBuilderType = match.functionBuilderType;
262+
if (functionBuilderType->isEqual(otherFunctionBuilderType))
263+
continue;
264+
265+
// We have at least two different function builder types.
266+
// Diagnose the ambiguity and provide potential solutions.
267+
decl->diagnose(
268+
diag::function_builder_infer_ambig, lookupDecl->getName(),
269+
functionBuilderType, otherFunctionBuilderType);
270+
decl->diagnose(diag::function_builder_infer_add_return)
271+
.fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n");
272+
for (const auto &match : matches) {
273+
decl->diagnose(
274+
diag::function_builder_infer_pick_specific,
275+
match.functionBuilderType,
276+
match.conformance->getProtocol()->getName())
277+
.fixItInsert(
278+
lookupDecl->getAttributeInsertionLoc(false),
279+
"@" + match.functionBuilderType.getString() + " ");
280+
}
281+
282+
return Type();
283+
}
284+
285+
return functionBuilderType;
286+
}
287+
185288
Type FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator,
186289
ValueDecl *decl) const {
187290
// Look for a function-builder custom attribute.
188291
auto attr = decl->getAttachedFunctionBuilder();
189-
if (!attr) return Type();
292+
if (!attr)
293+
return inferFunctionBuilderType(decl);
190294

191295
// Resolve a type for the attribute.
192296
auto mutableAttr = const_cast<CustomAttr*>(attr);

lib/Sema/TypeCheckStmt.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1862,7 +1862,8 @@ TypeCheckFunctionBodyUntilRequest::evaluate(Evaluator &evaluator,
18621862
if (auto *func = dyn_cast<FuncDecl>(AFD)) {
18631863
if (Type builderType = getFunctionBuilderType(func)) {
18641864
if (auto optBody =
1865-
TypeChecker::applyFunctionBuilderBodyTransform(func, builderType)) {
1865+
TypeChecker::applyFunctionBuilderBodyTransform(
1866+
func, builderType)) {
18661867
if (!*optBody)
18671868
return true;
18681869

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ bool typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD);
561561
Optional<BraceStmt *> applyFunctionBuilderBodyTransform(FuncDecl *func,
562562
Type builderType);
563563

564+
/// Find the return statements within the body of the given function.
565+
std::vector<ReturnStmt *> findReturnStatements(AnyFunctionRef fn);
566+
564567
bool typeCheckClosureBody(ClosureExpr *closure);
565568

566569
bool typeCheckTapBody(TapExpr *expr, DeclContext *DC);

0 commit comments

Comments
 (0)