Skip to content

Commit 0919b80

Browse files
authored
Merge pull request #27006 from DougGregor/one-way-constraints-5.1-08-28-2019
[5.1 08-28-2019] One way constraints
2 parents 78fe5a1 + 92f451e commit 0919b80

38 files changed

+1647
-498
lines changed

include/swift/AST/Expr.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5353,6 +5353,33 @@ class KeyPathDotExpr : public Expr {
53535353
}
53545354
};
53555355

5356+
/// Expression node that effects a "one-way" constraint in
5357+
/// the constraint system, allowing type information to flow from the
5358+
/// subexpression outward but not the other way.
5359+
///
5360+
/// One-way expressions are generally implicit and synthetic, introduced by
5361+
/// the type checker. However, there is a built-in expression of the
5362+
/// form \c Builtin.one_way(x) that forms a one-way constraint coming out
5363+
/// of expression `x` that can be used for testing purposes.
5364+
class OneWayExpr : public Expr {
5365+
Expr *SubExpr;
5366+
5367+
public:
5368+
/// Construct an implicit one-way expression from the given subexpression.
5369+
OneWayExpr(Expr *subExpr)
5370+
: Expr(ExprKind::OneWay, /*isImplicit=*/true), SubExpr(subExpr) { }
5371+
5372+
SourceLoc getLoc() const { return SubExpr->getLoc(); }
5373+
SourceRange getSourceRange() const { return SubExpr->getSourceRange(); }
5374+
5375+
Expr *getSubExpr() const { return SubExpr; }
5376+
void setSubExpr(Expr *subExpr) { SubExpr = subExpr; }
5377+
5378+
static bool classof(const Expr *E) {
5379+
return E->getKind() == ExprKind::OneWay;
5380+
}
5381+
};
5382+
53565383
inline bool Expr::isInfixOperator() const {
53575384
return isa<BinaryExpr>(this) || isa<IfExpr>(this) ||
53585385
isa<AssignExpr>(this) || isa<ExplicitCastExpr>(this);

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ EXPR(EditorPlaceholder, Expr)
190190
EXPR(ObjCSelector, Expr)
191191
EXPR(KeyPath, Expr)
192192
UNCHECKED_EXPR(KeyPathDot, Expr)
193+
UNCHECKED_EXPR(OneWay, Expr)
193194
EXPR(Tap, Expr)
194195
LAST_EXPR(Tap)
195196

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ namespace swift {
207207
/// before termination of the shrink phrase of the constraint solver.
208208
unsigned SolverShrinkUnsolvedThreshold = 10;
209209

210+
/// Enable one-way constraints in function builders.
211+
bool FunctionBuilderOneWayConstraints = true;
212+
210213
/// Disable the shrink phase of the expression type checker.
211214
bool SolverDisableShrink = false;
212215

include/swift/Basic/Statistics.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ FRONTEND_STATISTIC(Sema, NumLeafScopes)
175175
/// This is a measure of complexity of the contraction algorithm.
176176
FRONTEND_STATISTIC(Sema, NumConstraintsConsideredForEdgeContraction)
177177

178+
/// Number of constraint-solving scopes created in the typechecker, while
179+
/// solving expression type constraints. A rough proxy for "how much work the
180+
/// expression typechecker did".
181+
FRONTEND_STATISTIC(Sema, NumCyclicOneWayComponentsCollapsed)
182+
178183
/// Number of declarations that were deserialized. A rough proxy for the amount
179184
/// of material loaded from other modules.
180185
FRONTEND_STATISTIC(Sema, NumDeclsDeserialized)

include/swift/Option/FrontendOptions.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,14 @@ def Rmodule_interface_rebuild : Flag<["-"], "Rmodule-interface-rebuild">,
394394

395395
def solver_expression_time_threshold_EQ : Joined<["-"], "solver-expression-time-threshold=">;
396396

397+
def enable_function_builder_one_way_constraints : Flag<["-"],
398+
"enable-function-builder-one-way-constraints">,
399+
HelpText<"Enable one-way constraints in the function builder transformation">;
400+
401+
def disable_function_builder_one_way_constraints : Flag<["-"],
402+
"disable-function-builder-one-way-constraints">,
403+
HelpText<"Disable one-way constraints in the function builder transformation">;
404+
397405
def solver_disable_shrink :
398406
Flag<["-"], "solver-disable-shrink">,
399407
HelpText<"Disable the shrink phase of expression type checking">;

lib/AST/ASTDumper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,6 +2745,13 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
27452745
PrintWithColorRAII(OS, ParenthesisColor) << ')';
27462746
}
27472747

2748+
void visitOneWayExpr(OneWayExpr *E) {
2749+
printCommon(E, "one_way_expr");
2750+
OS << '\n';
2751+
printRec(E->getSubExpr());
2752+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
2753+
}
2754+
27482755
void visitTapExpr(TapExpr *E) {
27492756
printCommon(E, "tap_expr");
27502757
PrintWithColorRAII(OS, DeclColor) << " var=";

lib/AST/ASTWalker.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,18 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
10871087

10881088
Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) { return E; }
10891089

1090+
Expr *visitOneWayExpr(OneWayExpr *E) {
1091+
if (auto oldSubExpr = E->getSubExpr()) {
1092+
if (auto subExpr = doIt(oldSubExpr)) {
1093+
E->setSubExpr(subExpr);
1094+
} else {
1095+
return nullptr;
1096+
}
1097+
}
1098+
1099+
return E;
1100+
}
1101+
10901102
Expr *visitTapExpr(TapExpr *E) {
10911103
if (auto oldSubExpr = E->getSubExpr()) {
10921104
if (auto subExpr = doIt(oldSubExpr)) {

lib/AST/Expr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ ConcreteDeclRef Expr::getReferencedDecl() const {
366366
NO_REFERENCE(ObjCSelector);
367367
NO_REFERENCE(KeyPath);
368368
NO_REFERENCE(KeyPathDot);
369+
PASS_THROUGH_REFERENCE(OneWay, getSubExpr);
369370
NO_REFERENCE(Tap);
370371

371372
#undef SIMPLE_REFERENCE
@@ -537,6 +538,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
537538
case ExprKind::Error:
538539
case ExprKind::CodeCompletion:
539540
case ExprKind::LazyInitializer:
541+
case ExprKind::OneWay:
540542
return false;
541543

542544
case ExprKind::NilLiteral:

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
419419

420420
if (Args.getLastArg(OPT_solver_disable_shrink))
421421
Opts.SolverDisableShrink = true;
422+
Opts.FunctionBuilderOneWayConstraints =
423+
Args.hasFlag(OPT_enable_function_builder_one_way_constraints,
424+
OPT_disable_function_builder_one_way_constraints,
425+
/*Default=*/true);
422426

423427
if (const Arg *A = Args.getLastArg(OPT_value_recursion_threshold)) {
424428
unsigned threshold;

lib/Sema/BuilderTransform.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ class BuilderClosureVisitor
5050

5151
private:
5252
/// Produce a builder call to the given named function with the given arguments.
53-
CallExpr *buildCallIfWanted(SourceLoc loc,
54-
Identifier fnName, ArrayRef<Expr *> args,
55-
ArrayRef<Identifier> argLabels = {}) {
53+
Expr *buildCallIfWanted(SourceLoc loc,
54+
Identifier fnName, ArrayRef<Expr *> args,
55+
ArrayRef<Identifier> argLabels,
56+
bool allowOneWay) {
5657
if (!wantExpr)
5758
return nullptr;
5859

@@ -81,9 +82,17 @@ class BuilderClosureVisitor
8182
typeExpr, loc, fnName, DeclNameLoc(loc), /*implicit=*/true);
8283
SourceLoc openLoc = args.empty() ? loc : args.front()->getStartLoc();
8384
SourceLoc closeLoc = args.empty() ? loc : args.back()->getEndLoc();
84-
return CallExpr::create(ctx, memberRef, openLoc, args,
85-
argLabels, argLabelLocs, closeLoc,
86-
/*trailing closure*/ nullptr, /*implicit*/true);
85+
Expr *result = CallExpr::create(ctx, memberRef, openLoc, args,
86+
argLabels, argLabelLocs, closeLoc,
87+
/*trailing closure*/ nullptr,
88+
/*implicit*/true);
89+
90+
if (ctx.LangOpts.FunctionBuilderOneWayConstraints && allowOneWay) {
91+
// Form a one-way constraint to prevent backward propagation.
92+
result = new (ctx) OneWayExpr(result);
93+
}
94+
95+
return result;
8796
}
8897

8998
/// Check whether the builder supports the given operation.
@@ -160,12 +169,17 @@ class BuilderClosureVisitor
160169
}
161170

162171
auto expr = node.get<Expr *>();
172+
if (wantExpr && ctx.LangOpts.FunctionBuilderOneWayConstraints)
173+
expr = new (ctx) OneWayExpr(expr);
174+
163175
expressions.push_back(expr);
164176
}
165177

166178
// Call Builder.buildBlock(... args ...)
167179
return buildCallIfWanted(braceStmt->getStartLoc(),
168-
ctx.Id_buildBlock, expressions);
180+
ctx.Id_buildBlock, expressions,
181+
/*argLabels=*/{ },
182+
/*allowOneWay=*/true);
169183
}
170184

171185
Expr *visitReturnStmt(ReturnStmt *stmt) {
@@ -190,7 +204,8 @@ class BuilderClosureVisitor
190204
if (!arg)
191205
return nullptr;
192206

193-
return buildCallIfWanted(doStmt->getStartLoc(), ctx.Id_buildDo, arg);
207+
return buildCallIfWanted(doStmt->getStartLoc(), ctx.Id_buildDo, arg,
208+
/*argLabels=*/{ }, /*allowOneWay=*/true);
194209
}
195210

196211
CONTROL_FLOW_STMT(Yield)
@@ -275,7 +290,12 @@ class BuilderClosureVisitor
275290
// so we just need to call `buildIf` now, since we're at the top level.
276291
if (isOptional) {
277292
chainExpr = buildCallIfWanted(ifStmt->getStartLoc(),
278-
ctx.Id_buildIf, chainExpr);
293+
ctx.Id_buildIf, chainExpr,
294+
/*argLabels=*/{ },
295+
/*allowOneWay=*/true);
296+
} else if (ctx.LangOpts.FunctionBuilderOneWayConstraints) {
297+
// Form a one-way constraint to prevent backward propagation.
298+
chainExpr = new (ctx) OneWayExpr(chainExpr);
279299
}
280300

281301
return chainExpr;
@@ -395,7 +415,8 @@ class BuilderClosureVisitor
395415
bool isSecond = (path & 1);
396416
operand = buildCallIfWanted(operand->getStartLoc(),
397417
ctx.Id_buildEither, operand,
398-
{isSecond ? ctx.Id_second : ctx.Id_first});
418+
{isSecond ? ctx.Id_second : ctx.Id_first},
419+
/*allowOneWay=*/false);
399420
}
400421

401422
// Inject into Optional if required. We'll be adding the call to

0 commit comments

Comments
 (0)