Skip to content

Commit af3cb50

Browse files
committed
[Constraint solver] Introduce one-way constraints.
Introduce the notion of "one-way" binding constraints of the form $T0 one-way bind to $T1 which treats the type variables $T0 and $T1 as independent up until the point where $T1 simplifies down to a concrete type, at which point $T0 will be bound to that concrete type. $T0 won't be bound in any other way, so type information ends up being propagated right-to-left, only. This allows a constraint system to be broken up in more components that are solved independently. Specifically, the connected components algorithm now proceeds as follows: 1. Compute connected components, excluding one-way constraints from consideration. 2. Compute a directed graph amongst the components using only the one-way constraints, where an edge A -> B indicates that the type variables in component A need to be solved before those in component B. 3. Using the directed graph, compute the set of components that need to be solved before a given component. To utilize this, implement a new kind of solver step that handles the propagation of partial solutions across one-way constraints. This introduces a new kind of "split" within a connected component, where we collect each combination of partial solutions for the input components and (separately) try to solve the constraints in this component. Any correct solution from any of these attempts will then be recorded as a (partial) solution for this component. For example, consider: let _: Int8 = b ? Builtin.one_way(int8Or16(17)) : Builtin.one_way(int8Or16(42\ )) where int8Or16 is overloaded with types `(Int8) -> Int8` and `(Int16) -> Int16`. There are two one-way components (`int8Or16(17)`) and (`int8Or16(42)`), each of which can produce a value of type `Int8` or `Int16`. Those two components will be solved independently, and the partial solutions for each will be fed into the component that evaluates the ternary operator. There are four ways to attempt that evaluation: ``` [Int8, Int8] [Int8, Int16] [Int16, Int8] [Int16, Int16] To test this, introduce a new expression builtin `Builtin.one_way(x)` that introduces a one-way expression constraint binding the result of the expression 'x'. The builtin is meant to be used for testing purposes, and the one-way constraint expression itself can be synthesized by the type checker to introduce one-way constraints later on. Of these two, there are only two (partial) solutions that can work at all, because the types in the ternary operator need a common supertype: [Int8, Int8] [Int16, Int16] Therefore, these are the partial solutions that will be considered the results of the component containing the ternary expression. Note that only one of them meets the final constraint (convertibility to `Int8`), so the expression is well-formed. Part of rdar://problem/50150793. (cherry picked from commit 3c69f6a)
1 parent fdcf750 commit af3cb50

19 files changed

+810
-81
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

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/Sema/CSApply.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4742,6 +4742,10 @@ namespace {
47424742
llvm_unreachable("found KeyPathDotExpr in CSApply");
47434743
}
47444744

4745+
Expr *visitOneWayExpr(OneWayExpr *E) {
4746+
return E->getSubExpr();
4747+
}
4748+
47454749
Expr *visitTapExpr(TapExpr *E) {
47464750
auto type = simplifyType(cs.getType(E));
47474751

lib/Sema/CSBindings.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,18 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) {
632632
result.FullyBound = true;
633633
}
634634
break;
635+
636+
case ConstraintKind::OneWayBind: {
637+
// Don't produce any bindings if this type variable is on the left-hand
638+
// side of a one-way binding.
639+
auto firstType = constraint->getFirstType();
640+
if (auto *tv = firstType->getAs<TypeVariableType>()) {
641+
if (tv->getImpl().getRepresentative(nullptr) == typeVar)
642+
return {typeVar};
643+
}
644+
645+
break;
646+
}
635647
}
636648
}
637649

lib/Sema/CSGen.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,14 @@ namespace {
30263026
llvm_unreachable("found KeyPathDotExpr in CSGen");
30273027
}
30283028

3029+
Type visitOneWayExpr(OneWayExpr *expr) {
3030+
auto locator = CS.getConstraintLocator(expr);
3031+
auto resultTypeVar = CS.createTypeVariable(locator, 0);
3032+
CS.addConstraint(ConstraintKind::OneWayBind, resultTypeVar,
3033+
CS.getType(expr->getSubExpr()), locator);
3034+
return resultTypeVar;
3035+
}
3036+
30293037
Type visitTapExpr(TapExpr *expr) {
30303038
DeclContext *varDC = expr->getVar()->getDeclContext();
30313039
assert(varDC == CS.DC || (varDC && isa<AbstractClosureExpr>(varDC)) &&
@@ -3059,7 +3067,8 @@ namespace {
30593067
Join,
30603068
JoinInout,
30613069
JoinMeta,
3062-
JoinNonexistent
3070+
JoinNonexistent,
3071+
OneWay,
30633072
};
30643073

30653074
static TypeOperation getTypeOperation(UnresolvedDotExpr *UDE,
@@ -3073,6 +3082,7 @@ namespace {
30733082

30743083
return llvm::StringSwitch<TypeOperation>(
30753084
UDE->getName().getBaseIdentifier().str())
3085+
.Case("one_way", TypeOperation::OneWay)
30763086
.Case("type_join", TypeOperation::Join)
30773087
.Case("type_join_inout", TypeOperation::JoinInout)
30783088
.Case("type_join_meta", TypeOperation::JoinMeta)
@@ -3081,14 +3091,14 @@ namespace {
30813091
}
30823092

30833093
Type resultOfTypeOperation(TypeOperation op, Expr *Arg) {
3084-
auto *tuple = dyn_cast<TupleExpr>(Arg);
3085-
assert(tuple && "Expected argument tuple for join operations!");
3094+
auto *tuple = cast<TupleExpr>(Arg);
30863095

30873096
auto *lhs = tuple->getElement(0);
30883097
auto *rhs = tuple->getElement(1);
30893098

30903099
switch (op) {
30913100
case TypeOperation::None:
3101+
case TypeOperation::OneWay:
30923102
llvm_unreachable(
30933103
"We should have a valid type operation at this point!");
30943104

@@ -3452,18 +3462,23 @@ namespace {
34523462
/// Once we've visited the children of the given expression,
34533463
/// generate constraints from the expression.
34543464
Expr *walkToExprPost(Expr *expr) override {
3455-
3456-
// Handle the Builtin.type_join* family of calls by replacing
3457-
// them with dot_self_expr of type_expr with the type being the
3458-
// result of the join.
3465+
// Translate special type-checker Builtin calls into simpler expressions.
34593466
if (auto *apply = dyn_cast<ApplyExpr>(expr)) {
34603467
auto fnExpr = apply->getFn();
34613468
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(fnExpr)) {
34623469
auto &CS = CG.getConstraintSystem();
34633470
auto typeOperation =
34643471
ConstraintGenerator::getTypeOperation(UDE, CS.getASTContext());
34653472

3466-
if (typeOperation != ConstraintGenerator::TypeOperation::None) {
3473+
if (typeOperation == ConstraintGenerator::TypeOperation::OneWay) {
3474+
// For a one-way constraint, create the OneWayExpr node.
3475+
auto *arg = cast<ParenExpr>(apply->getArg())->getSubExpr();
3476+
expr = new (CS.getASTContext()) OneWayExpr(arg);
3477+
} else if (typeOperation !=
3478+
ConstraintGenerator::TypeOperation::None) {
3479+
// Handle the Builtin.type_join* family of calls by replacing
3480+
// them with dot_self_expr of type_expr with the type being the
3481+
// result of the join.
34673482
auto joinMetaTy =
34683483
CG.resultOfTypeOperation(typeOperation, apply->getArg());
34693484
auto joinTy = joinMetaTy->castTo<MetatypeType>()->getInstanceType();

lib/Sema/CSSimplify.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
11001100
case ConstraintKind::BridgingConversion:
11011101
case ConstraintKind::FunctionInput:
11021102
case ConstraintKind::FunctionResult:
1103+
case ConstraintKind::OneWayBind:
11031104
llvm_unreachable("Not a conversion");
11041105
}
11051106

@@ -1162,6 +1163,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
11621163
case ConstraintKind::ValueMember:
11631164
case ConstraintKind::FunctionInput:
11641165
case ConstraintKind::FunctionResult:
1166+
case ConstraintKind::OneWayBind:
11651167
return false;
11661168
}
11671169

@@ -1338,6 +1340,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
13381340
case ConstraintKind::BridgingConversion:
13391341
case ConstraintKind::FunctionInput:
13401342
case ConstraintKind::FunctionResult:
1343+
case ConstraintKind::OneWayBind:
13411344
llvm_unreachable("Not a relational constraint");
13421345
}
13431346

@@ -2484,6 +2487,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
24842487
case ConstraintKind::ValueMember:
24852488
case ConstraintKind::FunctionInput:
24862489
case ConstraintKind::FunctionResult:
2490+
case ConstraintKind::OneWayBind:
24872491
llvm_unreachable("Not a relational constraint");
24882492
}
24892493
}
@@ -4984,6 +4988,29 @@ ConstraintSystem::simplifyDefaultableConstraint(
49844988
return SolutionKind::Solved;
49854989
}
49864990

4991+
ConstraintSystem::SolutionKind
4992+
ConstraintSystem::simplifyOneWayConstraint(
4993+
ConstraintKind kind,
4994+
Type first, Type second, TypeMatchOptions flags,
4995+
ConstraintLocatorBuilder locator) {
4996+
// Determine whether the second type can be fully simplified. Only then
4997+
// will this constraint be resolved.
4998+
Type secondSimplified = simplifyType(second);
4999+
if (secondSimplified->hasTypeVariable()) {
5000+
if (flags.contains(TMF_GenerateConstraints)) {
5001+
addUnsolvedConstraint(
5002+
Constraint::create(*this, kind, first, second,
5003+
getConstraintLocator(locator)));
5004+
return SolutionKind::Solved;
5005+
}
5006+
5007+
return SolutionKind::Unsolved;
5008+
}
5009+
5010+
// Translate this constraint into a one-way binding constraint.
5011+
return matchTypes(first, secondSimplified, ConstraintKind::Bind, flags,
5012+
locator);
5013+
}
49875014

49885015
ConstraintSystem::SolutionKind
49895016
ConstraintSystem::simplifyDynamicTypeOfConstraint(
@@ -6893,6 +6920,9 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
68936920
return simplifyFunctionComponentConstraint(kind, first, second,
68946921
subflags, locator);
68956922

6923+
case ConstraintKind::OneWayBind:
6924+
return simplifyOneWayConstraint(kind, first, second, subflags, locator);
6925+
68966926
case ConstraintKind::ValueMember:
68976927
case ConstraintKind::UnresolvedValueMember:
68986928
case ConstraintKind::BindOverload:
@@ -7248,6 +7278,13 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
72487278
case ConstraintKind::Disjunction:
72497279
// Disjunction constraints are never solved here.
72507280
return SolutionKind::Unsolved;
7281+
7282+
case ConstraintKind::OneWayBind:
7283+
return simplifyOneWayConstraint(constraint.getKind(),
7284+
constraint.getFirstType(),
7285+
constraint.getSecondType(),
7286+
TMF_GenerateConstraints,
7287+
constraint.getLocator());
72517288
}
72527289

72537290
llvm_unreachable("Unhandled ConstraintKind in switch.");

lib/Sema/CSSolver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) {
16561656
case ConstraintKind::SelfObjectOfProtocol:
16571657
case ConstraintKind::ConformsTo:
16581658
case ConstraintKind::Defaultable:
1659+
case ConstraintKind::OneWayBind:
16591660
break;
16601661
}
16611662
}

0 commit comments

Comments
 (0)