Skip to content

Commit 952eb9d

Browse files
committed
Allow function-builder attributes on funcs and computed vars.
rdar://50150690
1 parent 5d0d8d9 commit 952eb9d

File tree

11 files changed

+178
-75
lines changed

11 files changed

+178
-75
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2709,6 +2709,14 @@ class ValueDecl : public Decl {
27092709
/// `this` must be of a decl type that supports opaque return types, and
27102710
/// must not have previously had an opaque result type set.
27112711
void setOpaqueResultTypeDecl(OpaqueTypeDecl *D);
2712+
2713+
/// Retrieve the attribute associating this declaration with a
2714+
/// function builder, if there is one.
2715+
CustomAttr *getAttachedFunctionBuilder() const;
2716+
2717+
/// Retrieve the @functionBuilder type attached to this declaration,
2718+
/// if there is one.
2719+
Type getFunctionBuilderType() const;
27122720
};
27132721

27142722
/// This is a common base class for declarations which declare a type.
@@ -5335,14 +5343,6 @@ class ParamDecl : public VarDecl {
53355343
return getVarargBaseTy(getInterfaceType());
53365344
}
53375345

5338-
/// Retrieve the attribute marking this as a function builder parameter,
5339-
/// if there is one.
5340-
CustomAttr *getAttachedFunctionBuilder() const;
5341-
5342-
/// Retrieve the @functionBuilder type attached to this parameter,
5343-
/// if there is one.
5344-
Type getFunctionBuilderType() const;
5345-
53465346
SourceRange getSourceRange() const;
53475347

53485348
AnyFunctionType::Param toFunctionParam(Type type = Type()) const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4480,9 +4480,13 @@ NOTE(note_function_builder_control_flow, none,
44804480
"builder %0", (DeclName))
44814481
ERROR(function_builder_reserved_name, none,
44824482
"function builder type name must start with an uppercase letter", ())
4483-
ERROR(function_builder_attribute_not_on_parameter, none,
4484-
"function builder attribute %0 can only be applied to a parameter",
4485-
(DeclName))
4483+
ERROR(function_builder_attribute_not_allowed_here, none,
4484+
"function builder attribute %0 can only be applied to a parameter, "
4485+
"function, or storage with a getter", (DeclName))
4486+
ERROR(function_builder_attribute_on_storage_without_getter, none,
4487+
"function builder attribute %0 can only be applied to a "
4488+
"%select{subscript|property|constant|variable}1 if it defines a getter",
4489+
(DeclName, unsigned))
44864490
ERROR(function_builder_parameter_not_of_function_type, none,
44874491
"function builder attribute %0 can only be applied to a parameter of "
44884492
"function type",
@@ -4492,7 +4496,8 @@ ERROR(function_builder_parameter_autoclosure, none,
44924496
"parameter",
44934497
(DeclName))
44944498
ERROR(function_builder_multiple, none,
4495-
"only one function builder attribute can be attached to a parameter", ())
4499+
"only one function builder attribute can be attached to a "
4500+
"%select{declaration|parameter}0", (bool))
44964501
NOTE(previous_function_builder_here, none,
44974502
"previous function builder specified here", ())
44984503
ERROR(function_builder_arguments, none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
namespace swift {
3131

3232
class GenericParamList;
33-
class ParamDecl;
3433
struct PropertyWrapperBackingPropertyInfo;
3534
class RequirementRepr;
3635
class SpecializeAttr;
3736
class TypeAliasDecl;
3837
struct TypeLoc;
38+
class ValueDecl;
3939

4040
/// Display a nominal type or extension thereof.
4141
void simple_display(
@@ -624,12 +624,12 @@ class StructuralTypeRequest :
624624
};
625625

626626
/// Request the custom attribute which attaches a function builder to the
627-
/// given parameter.
627+
/// given declaration.
628628
class AttachedFunctionBuilderRequest :
629629
public SimpleRequest<AttachedFunctionBuilderRequest,
630630
CacheKind::Cached,
631631
CustomAttr *,
632-
ParamDecl *> {
632+
ValueDecl *> {
633633
public:
634634
using SimpleRequest::SimpleRequest;
635635

@@ -638,7 +638,7 @@ class AttachedFunctionBuilderRequest :
638638

639639
// Evaluation.
640640
llvm::Expected<CustomAttr *>
641-
evaluate(Evaluator &evaluator, ParamDecl *) const;
641+
evaluate(Evaluator &evaluator, ValueDecl *decl) const;
642642

643643
public:
644644
// Caching
@@ -649,22 +649,21 @@ class AttachedFunctionBuilderRequest :
649649
void noteCycleStep(DiagnosticEngine &diags) const;
650650
};
651651

652-
/// Request the type spelled out in a custom attribute.
653-
///
654-
/// Different parameters cannot be used for the same attribute.
652+
/// Request the function builder type attached to the given declaration,
653+
/// if any.
655654
class FunctionBuilderTypeRequest :
656655
public SimpleRequest<FunctionBuilderTypeRequest,
657656
CacheKind::Cached,
658657
Type,
659-
ParamDecl *> {
658+
ValueDecl *> {
660659
public:
661660
using SimpleRequest::SimpleRequest;
662661

663662
private:
664663
friend SimpleRequest;
665664

666665
llvm::Expected<Type>
667-
evaluate(Evaluator &evaluator, ParamDecl *param) const;
666+
evaluate(Evaluator &evaluator, ValueDecl *decl) const;
668667

669668
public:
670669
// Caching

lib/AST/Decl.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5740,25 +5740,25 @@ void ParamDecl::setStoredProperty(VarDecl *var) {
57405740
DefaultValueAndFlags.getPointer()->DefaultArg = var;
57415741
}
57425742

5743-
Type ParamDecl::getFunctionBuilderType() const {
5744-
// Fast path: most parameters do not have any attributes at all,
5745-
// much less custom ones.
5743+
Type ValueDecl::getFunctionBuilderType() const {
5744+
// Fast path: most declarations (especially parameters, which is where
5745+
// this is hottest) do not have any custom attributes at all.
57465746
if (!getAttrs().hasAttribute<CustomAttr>()) return Type();
57475747

57485748
auto &ctx = getASTContext();
5749-
auto mutableThis = const_cast<ParamDecl *>(this);
5749+
auto mutableThis = const_cast<ValueDecl *>(this);
57505750
return evaluateOrDefault(ctx.evaluator,
57515751
FunctionBuilderTypeRequest{mutableThis},
57525752
Type());
57535753
}
57545754

5755-
CustomAttr *ParamDecl::getAttachedFunctionBuilder() const {
5756-
// Fast path: most parameters do not have any attributes at all,
5757-
// much less custom ones.
5755+
CustomAttr *ValueDecl::getAttachedFunctionBuilder() const {
5756+
// Fast path: most declarations (especially parameters, which is where
5757+
// this is hottest) do not have any custom attributes at all.
57585758
if (!getAttrs().hasAttribute<CustomAttr>()) return nullptr;
57595759

57605760
auto &ctx = getASTContext();
5761-
auto mutableThis = const_cast<ParamDecl *>(this);
5761+
auto mutableThis = const_cast<ValueDecl *>(this);
57625762
return evaluateOrDefault(ctx.evaluator,
57635763
AttachedFunctionBuilderRequest{mutableThis},
57645764
nullptr);

lib/Sema/ConstraintSystem.cpp

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,7 +2769,7 @@ namespace {
27692769
/// Visitor to classify the contents of the given closure.
27702770
class BuilderClosureVisitor
27712771
: public StmtVisitor<BuilderClosureVisitor, Expr *> {
2772-
ConstraintSystem &cs;
2772+
ConstraintSystem *cs;
27732773
ASTContext &ctx;
27742774
bool wantExpr;
27752775
Type builderType;
@@ -2795,10 +2795,10 @@ namespace {
27952795
builderType->hasTypeVariable() ? Type() : builderType);
27962796

27972797
auto typeExpr = new (ctx) TypeExpr(typeLoc);
2798-
cs.setType(typeExpr, builderType);
2798+
if (cs) cs->setType(typeExpr, builderType);
27992799
typeExpr->setImplicit();
28002800
auto memberRef = new (ctx) UnresolvedDotExpr(
2801-
typeExpr, loc, fnName, DeclNameLoc(), /*implicit=*/true);
2801+
typeExpr, loc, fnName, DeclNameLoc(loc), /*implicit=*/true);
28022802
return CallExpr::createImplicit(ctx, memberRef, args, argLabels);
28032803
}
28042804

@@ -2835,9 +2835,12 @@ namespace {
28352835
}
28362836

28372837
public:
2838-
BuilderClosureVisitor(ConstraintSystem &cs, bool wantExpr, Type builderType)
2839-
: cs(cs), ctx(cs.getASTContext()), wantExpr(wantExpr),
2840-
builderType(builderType) {
2838+
BuilderClosureVisitor(ASTContext &ctx, ConstraintSystem *cs,
2839+
bool wantExpr, Type builderType)
2840+
: cs(cs), ctx(ctx), wantExpr(wantExpr), builderType(builderType) {
2841+
assert((cs || !builderType->hasTypeVariable()) &&
2842+
"cannot handle builder type with type variables without "
2843+
"constraint system");
28412844
builder = builderType->getAnyNominal();
28422845
}
28432846

@@ -3186,6 +3189,37 @@ static bool hasReturnStmt(Stmt *stmt) {
31863189
return finder.hasReturnStmt;
31873190
}
31883191

3192+
bool TypeChecker::typeCheckFunctionBuilderFuncBody(FuncDecl *FD,
3193+
Type builderType) {
3194+
// Try to build a single result expression.
3195+
BuilderClosureVisitor visitor(Context, nullptr,
3196+
/*wantExpr=*/true, builderType);
3197+
Expr *returnExpr = visitor.visit(FD->getBody());
3198+
if (!returnExpr)
3199+
return true;
3200+
3201+
// Make sure we have a usable result type for the body.
3202+
Type returnType = AnyFunctionRef(FD).getBodyResultType();
3203+
if (!returnType || returnType->hasError())
3204+
return true;
3205+
3206+
// Type-check the single result expression.
3207+
Type returnExprType = typeCheckExpression(returnExpr, FD,
3208+
TypeLoc::withoutLoc(returnType),
3209+
CTP_ReturnStmt);
3210+
if (!returnExprType)
3211+
return true;
3212+
assert(returnExprType->isEqual(returnType));
3213+
3214+
auto returnStmt = new (Context) ReturnStmt(SourceLoc(), returnExpr);
3215+
auto origBody = FD->getBody();
3216+
auto fakeBody = BraceStmt::create(Context, origBody->getLBraceLoc(),
3217+
{ returnStmt },
3218+
origBody->getRBraceLoc());
3219+
FD->setBody(fakeBody);
3220+
return false;
3221+
}
3222+
31893223
ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder(
31903224
ClosureExpr *closure, Type builderType, ConstraintLocator *calleeLocator,
31913225
ConstraintLocatorBuilder locator) {
@@ -3208,7 +3242,8 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder(
32083242
}
32093243

32103244
// Check whether we can apply this function builder.
3211-
BuilderClosureVisitor visitor(*this, /*wantExpr=*/false, builderType);
3245+
BuilderClosureVisitor visitor(getASTContext(), this,
3246+
/*wantExpr=*/false, builderType);
32123247
(void)visitor.visit(closure->getBody());
32133248

32143249

@@ -3246,7 +3281,8 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::applyFunctionBuilder(
32463281
assert(!builderType->hasTypeParameter());
32473282
}
32483283

3249-
BuilderClosureVisitor visitor(*this, /*wantExpr=*/true, builderType);
3284+
BuilderClosureVisitor visitor(getASTContext(), this,
3285+
/*wantExpr=*/true, builderType);
32503286
Expr *singleExpr = visitor.visit(closure->getBody());
32513287

32523288
if (TC.precheckedClosures.insert(closure).second &&

lib/Sema/TypeCheckAttr.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,12 +2566,29 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
25662566
}
25672567

25682568
// If the nominal type is a function builder type, verify that D is a
2569-
// parameter of function type.
2569+
// function, storage with an explicit getter, or parameter of function type.
25702570
if (nominal->getAttrs().hasAttribute<FunctionBuilderAttr>()) {
2571-
auto param = dyn_cast<ParamDecl>(D);
2572-
if (!param) {
2571+
ValueDecl *decl;
2572+
if (auto param = dyn_cast<ParamDecl>(D)) {
2573+
decl = param;
2574+
} else if (auto func = dyn_cast<FuncDecl>(D)) {
2575+
decl = func;
2576+
} else if (auto storage = dyn_cast<AbstractStorageDecl>(D)) {
2577+
decl = storage;
2578+
auto getter = storage->getGetter();
2579+
if (!getter || getter->isImplicit() || !getter->hasBody()) {
2580+
TC.diagnose(attr->getLocation(),
2581+
diag::function_builder_attribute_on_storage_without_getter,
2582+
nominal->getFullName(),
2583+
isa<SubscriptDecl>(storage) ? 0
2584+
: storage->getDeclContext()->isTypeContext() ? 1
2585+
: cast<VarDecl>(storage)->isLet() ? 2 : 3);
2586+
attr->setInvalid();
2587+
return;
2588+
}
2589+
} else {
25732590
TC.diagnose(attr->getLocation(),
2574-
diag::function_builder_attribute_not_on_parameter,
2591+
diag::function_builder_attribute_not_allowed_here,
25752592
nominal->getFullName());
25762593
attr->setInvalid();
25772594
return;
@@ -2584,17 +2601,18 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
25842601
}
25852602

25862603
// Complain if this isn't the primary function-builder attribute.
2587-
auto attached = param->getAttachedFunctionBuilder();
2604+
auto attached = decl->getAttachedFunctionBuilder();
25882605
if (attached != attr) {
2589-
TC.diagnose(attr->getLocation(), diag::function_builder_multiple);
2606+
TC.diagnose(attr->getLocation(), diag::function_builder_multiple,
2607+
isa<ParamDecl>(decl));
25902608
TC.diagnose(attached->getLocation(),
25912609
diag::previous_function_builder_here);
25922610
attr->setInvalid();
25932611
return;
25942612
} else {
25952613
// Force any diagnostics associated with computing the function-builder
25962614
// type.
2597-
(void) param->getFunctionBuilderType();
2615+
(void) decl->getFunctionBuilderType();
25982616
}
25992617

26002618
return;

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ EnumRawTypeRequest::evaluate(Evaluator &evaluator, EnumDecl *enumDecl,
150150

151151
llvm::Expected<CustomAttr *>
152152
AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator,
153-
ParamDecl *param) const {
154-
ASTContext &ctx = param->getASTContext();
155-
auto dc = param->getDeclContext();
156-
for (auto attr : param->getAttrs().getAttributes<CustomAttr>()) {
153+
ValueDecl *decl) const {
154+
ASTContext &ctx = decl->getASTContext();
155+
auto dc = decl->getDeclContext();
156+
for (auto attr : decl->getAttrs().getAttributes<CustomAttr>()) {
157157
auto mutableAttr = const_cast<CustomAttr *>(attr);
158158
// Figure out which nominal declaration this custom attribute refers to.
159159
auto nominal = evaluateOrDefault(ctx.evaluator,
@@ -174,14 +174,14 @@ AttachedFunctionBuilderRequest::evaluate(Evaluator &evaluator,
174174

175175
llvm::Expected<Type>
176176
FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator,
177-
ParamDecl *param) const {
177+
ValueDecl *decl) const {
178178
// Look for a function-builder custom attribute.
179-
auto attr = param->getAttachedFunctionBuilder();
179+
auto attr = decl->getAttachedFunctionBuilder();
180180
if (!attr) return Type();
181181

182182
// Resolve a type for the attribute.
183183
auto mutableAttr = const_cast<CustomAttr*>(attr);
184-
auto dc = param->getDeclContext();
184+
auto dc = decl->getDeclContext();
185185
auto &ctx = dc->getASTContext();
186186
Type type = resolveCustomAttrType(mutableAttr, dc,
187187
CustomAttrTypeKind::NonGeneric);
@@ -193,26 +193,30 @@ FunctionBuilderTypeRequest::evaluate(Evaluator &evaluator,
193193
return Type();
194194
}
195195

196-
// The parameter had better already have an interface type.
197-
Type paramType = param->getInterfaceType();
198-
assert(paramType);
199-
auto paramFnType = paramType->getAs<FunctionType>();
200-
201-
// Require the parameter to be an interface type.
202-
if (!paramFnType) {
203-
ctx.Diags.diagnose(attr->getLocation(),
204-
diag::function_builder_parameter_not_of_function_type,
205-
nominal->getFullName());
206-
mutableAttr->setInvalid();
207-
return Type();
208-
}
196+
// Do some additional checking on parameters.
197+
if (auto param = dyn_cast<ParamDecl>(decl)) {
198+
// The parameter had better already have an interface type.
199+
Type paramType = param->getInterfaceType();
200+
assert(paramType);
201+
auto paramFnType = paramType->getAs<FunctionType>();
202+
203+
// Require the parameter to be an interface type.
204+
if (!paramFnType) {
205+
ctx.Diags.diagnose(attr->getLocation(),
206+
diag::function_builder_parameter_not_of_function_type,
207+
nominal->getFullName());
208+
mutableAttr->setInvalid();
209+
return Type();
210+
}
209211

210-
if (param->isAutoClosure()) {
211-
ctx.Diags.diagnose(attr->getLocation(),
212-
diag::function_builder_parameter_autoclosure,
213-
nominal->getFullName());
214-
mutableAttr->setInvalid();
215-
return Type();
212+
// Forbid the parameter to be an autoclosure.
213+
if (param->isAutoClosure()) {
214+
ctx.Diags.diagnose(attr->getLocation(),
215+
diag::function_builder_parameter_autoclosure,
216+
nominal->getFullName());
217+
mutableAttr->setInvalid();
218+
return Type();
219+
}
216220
}
217221

218222
return type->mapTypeOutOfContext();

0 commit comments

Comments
 (0)