mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[DSL] Allow function builders to opt in to "if" statements.
If a function builder contains a buildIf function, then "if" statements will be supported by passing an optional of the "then" branch. "if" statements with an "else" statement are unsupported at present.
This commit is contained in:
@@ -32,6 +32,7 @@ IDENTIFIER(ArrayLiteralElement)
|
||||
IDENTIFIER(atIndexedSubscript)
|
||||
IDENTIFIER_(bridgeToObjectiveC)
|
||||
IDENTIFIER(buildBlock)
|
||||
IDENTIFIER(buildIf)
|
||||
IDENTIFIER(buildDo)
|
||||
IDENTIFIER(Change)
|
||||
IDENTIFIER_WITH_NAME(code_, "_code")
|
||||
|
||||
@@ -117,6 +117,8 @@ namespace {
|
||||
return getStartLocImpl(E);
|
||||
}
|
||||
template <class T> static SourceRange getSourceRange(const T *E) {
|
||||
if (E->getStartLoc().isInvalid() != E->getEndLoc().isInvalid())
|
||||
return SourceRange();
|
||||
return { E->getStartLoc(), E->getEndLoc() };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2871,7 +2871,51 @@ namespace {
|
||||
|
||||
CONTROL_FLOW_STMT(Yield)
|
||||
CONTROL_FLOW_STMT(Defer)
|
||||
CONTROL_FLOW_STMT(If)
|
||||
|
||||
static Expr *getTrivialBooleanCondition(StmtCondition condition) {
|
||||
if (condition.size() != 1)
|
||||
return nullptr;
|
||||
|
||||
return condition.front().getBooleanOrNull();
|
||||
}
|
||||
|
||||
Expr *visitIfStmt(IfStmt *ifStmt) {
|
||||
if (!builderSupports(ctx.Id_buildIf) ||
|
||||
ifStmt->getElseStmt() ||
|
||||
!getTrivialBooleanCondition(ifStmt->getCond())) {
|
||||
this->controlFlowStmt = ifStmt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto thenArg = visit(ifStmt->getThenStmt());
|
||||
if (!thenArg)
|
||||
return nullptr;
|
||||
|
||||
if (!wantExpr)
|
||||
return nullptr;
|
||||
|
||||
auto optionalDecl = ctx.getOptionalDecl();
|
||||
auto optionalType = optionalDecl->getDeclaredType();
|
||||
|
||||
auto optionalTypeExpr = TypeExpr::createImplicit(optionalType, ctx);
|
||||
auto someRef = new (ctx) UnresolvedDotExpr(
|
||||
optionalTypeExpr, SourceLoc(), ctx.getIdentifier("some"),
|
||||
DeclNameLoc(), /*implicit=*/true);
|
||||
auto someCall = CallExpr::createImplicit(ctx, someRef, thenArg, { });
|
||||
|
||||
optionalTypeExpr = TypeExpr::createImplicit(optionalType, ctx);
|
||||
auto noneRef = new (ctx) UnresolvedDotExpr(
|
||||
optionalTypeExpr, ifStmt->getEndLoc(), ctx.getIdentifier("none"),
|
||||
DeclNameLoc(ifStmt->getEndLoc()), /*implicit=*/true);
|
||||
|
||||
auto ifExpr = new (ctx) IfExpr(
|
||||
getTrivialBooleanCondition(ifStmt->getCond()),
|
||||
SourceLoc(), someCall,
|
||||
SourceLoc(), noneRef);
|
||||
ifExpr->setImplicit();
|
||||
return buildCallIfWanted(ctx.Id_buildIf, ifExpr);
|
||||
}
|
||||
|
||||
CONTROL_FLOW_STMT(Guard)
|
||||
CONTROL_FLOW_STMT(While)
|
||||
CONTROL_FLOW_STMT(DoCatch)
|
||||
|
||||
@@ -24,15 +24,16 @@ struct TupleBuilder {
|
||||
}
|
||||
|
||||
static func buildDo<T>(_ value: T) -> T { return value }
|
||||
static func buildIf<T>(_ value: T?) -> T? { return value }
|
||||
}
|
||||
|
||||
func tuplify<T>(@TupleBuilder body: () -> T) {
|
||||
print(body())
|
||||
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) {
|
||||
print(body(cond))
|
||||
}
|
||||
|
||||
// CHECK: (17, 3.14159, "Hello, DSL", (["nested", "do"], 6))
|
||||
// CHECK: (17, 3.14159, "Hello, DSL", (["nested", "do"], 6), Optional((2.71828, ["if", "stmt"])))
|
||||
let name = "dsl"
|
||||
tuplify {
|
||||
tuplify(true) {
|
||||
17
|
||||
3.14159
|
||||
"Hello, \(name.map { $0.uppercased() }.joined())"
|
||||
@@ -40,4 +41,17 @@ tuplify {
|
||||
["nested", "do"]
|
||||
1 + 2 + 3
|
||||
}
|
||||
if $0 {
|
||||
2.71828
|
||||
["if", "stmt"]
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: ("Empty optional", nil)
|
||||
tuplify(false) {
|
||||
"Empty optional"
|
||||
if $0 {
|
||||
2.71828
|
||||
["if", "stmt"]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user