Skip to content

Commit 64f903f

Browse files
committed
[Type checker] Experimental support for one-way parameter constraints.
Introduce an experimental mode (behind the flag `experimental-one-way-closure-params`) that places one-way constraints between closure parameter types and references to those parameters within the body of the closure. The intent here is to break up constraint systems further, potentially improving type checking performance and making way for larger closure bodies to be supported. This is a source-breaking change when the body of a single-expression closure is used to determine the parameter types. One obvious example is when there is no contextual type, e.g., let _ = { $0 + 1 } this type-checks today because `1` becomes `Int`, which matches the `+` overload with the type `(Int, Int) -> Int`, determining the parameter type `Int` for the closure. Such code would not type-check with one-way constraints.
1 parent 7d4da10 commit 64f903f

File tree

9 files changed

+73
-7
lines changed

9 files changed

+73
-7
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,10 @@ namespace swift {
549549
/// Enable constraint solver support for experimental
550550
/// operator protocol designator feature.
551551
bool SolverEnableOperatorDesignatedTypes = false;
552-
552+
553+
/// Enable experimental support for one-way constraints for the
554+
/// parameters of closures.
555+
bool EnableOneWayClosureParameters = false;
553556
};
554557
} // end namespace swift
555558

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,10 @@ def experimental_print_full_convention :
642642
HelpText<"When emitting a module interface, emit additional @convention "
643643
"arguments, regardless of whether they were written in the source">;
644644

645+
def experimental_one_way_closure_params :
646+
Flag<["-"], "experimental-one-way-closure-params">,
647+
HelpText<"Enable experimental support for one-way closure parameters">;
648+
645649
def prebuilt_module_cache_path :
646650
Separate<["-"], "prebuilt-module-cache-path">,
647651
HelpText<"Directory of prebuilt modules for loading module interfaces">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,8 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
724724

725725
Opts.SolverEnableOperatorDesignatedTypes |=
726726
Args.hasArg(OPT_solver_enable_operator_designated_types);
727+
Opts.EnableOneWayClosureParameters |=
728+
Args.hasArg(OPT_experimental_one_way_closure_params);
727729

728730
Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints);
729731
Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures);

lib/Sema/CSBindings.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
710710
}
711711
break;
712712

713-
case ConstraintKind::OneWayEqual: {
713+
case ConstraintKind::OneWayEqual:
714+
case ConstraintKind::OneWayBindParam: {
714715
// Don't produce any bindings if this type variable is on the left-hand
715716
// side of a one-way binding.
716717
auto firstType = constraint->getFirstType();

lib/Sema/CSSimplify.cpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
13221322
case ConstraintKind::FunctionInput:
13231323
case ConstraintKind::FunctionResult:
13241324
case ConstraintKind::OneWayEqual:
1325+
case ConstraintKind::OneWayBindParam:
13251326
case ConstraintKind::DefaultClosureType:
13261327
llvm_unreachable("Not a conversion");
13271328
}
@@ -1387,6 +1388,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
13871388
case ConstraintKind::FunctionInput:
13881389
case ConstraintKind::FunctionResult:
13891390
case ConstraintKind::OneWayEqual:
1391+
case ConstraintKind::OneWayBindParam:
13901392
case ConstraintKind::DefaultClosureType:
13911393
return false;
13921394
}
@@ -1699,6 +1701,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
16991701
case ConstraintKind::FunctionInput:
17001702
case ConstraintKind::FunctionResult:
17011703
case ConstraintKind::OneWayEqual:
1704+
case ConstraintKind::OneWayBindParam:
17021705
case ConstraintKind::DefaultClosureType:
17031706
llvm_unreachable("Not a relational constraint");
17041707
}
@@ -4406,6 +4409,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
44064409
case ConstraintKind::FunctionInput:
44074410
case ConstraintKind::FunctionResult:
44084411
case ConstraintKind::OneWayEqual:
4412+
case ConstraintKind::OneWayBindParam:
44094413
case ConstraintKind::DefaultClosureType:
44104414
llvm_unreachable("Not a relational constraint");
44114415
}
@@ -7121,9 +7125,16 @@ ConstraintSystem::simplifyOneWayConstraint(
71217125
return SolutionKind::Solved;
71227126
}
71237127

7124-
// Translate this constraint into a one-way binding constraint.
7125-
return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags,
7126-
locator);
7128+
// Translate this constraint into an equality or bind-parameter constraint,
7129+
// as appropriate.
7130+
if (kind == ConstraintKind::OneWayEqual) {
7131+
return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags,
7132+
locator);
7133+
}
7134+
7135+
assert(kind == ConstraintKind::OneWayBindParam);
7136+
return matchTypes(
7137+
secondSimplified, first, ConstraintKind::BindParam, flags, locator);
71277138
}
71287139

71297140
static Type getFunctionBuilderTypeFor(ConstraintSystem &cs, unsigned paramIdx,
@@ -7175,12 +7186,27 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71757186

71767187
Type internalType;
71777188

7189+
bool oneWayConstraints =
7190+
getASTContext().TypeCheckerOpts.EnableOneWayClosureParameters;
71787191
if (paramList->get(i)->getTypeRepr()) {
71797192
// Internal type is the type used in the body of the closure,
71807193
// so "external" type translates to it as follows:
71817194
// - `Int...` -> `[Int]`,
71827195
// - `inout Int` -> `@lvalue Int`.
71837196
internalType = param.getParameterType();
7197+
7198+
// When there are type variables in the type and we have enabled
7199+
// one-way constraints, create a fresh type variable to handle the
7200+
// binding.
7201+
if (oneWayConstraints && internalType->hasTypeVariable()) {
7202+
auto *paramLoc =
7203+
getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
7204+
auto *typeVar = createTypeVariable(paramLoc, TVO_CanBindToLValue |
7205+
TVO_CanBindToNoEscape);
7206+
addConstraint(
7207+
ConstraintKind::OneWayBindParam, typeVar, internalType, paramLoc);
7208+
internalType = typeVar;
7209+
}
71847210
} else {
71857211
auto *paramLoc =
71867212
getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
@@ -7194,7 +7220,13 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71947220
param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar);
71957221

71967222
auto externalType = param.getOldType();
7197-
addConstraint(ConstraintKind::BindParam, externalType, typeVar, paramLoc);
7223+
if (oneWayConstraints) {
7224+
addConstraint(
7225+
ConstraintKind::OneWayBindParam, typeVar, externalType, paramLoc);
7226+
} else {
7227+
addConstraint(
7228+
ConstraintKind::BindParam, externalType, typeVar, paramLoc);
7229+
}
71987230
}
71997231

72007232
setType(paramList->get(i), internalType);
@@ -9759,6 +9791,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
97599791
subflags, locator);
97609792

97619793
case ConstraintKind::OneWayEqual:
9794+
case ConstraintKind::OneWayBindParam:
97629795
return simplifyOneWayConstraint(kind, first, second, subflags, locator);
97639796

97649797
case ConstraintKind::ValueMember:
@@ -10271,6 +10304,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
1027110304
return SolutionKind::Unsolved;
1027210305

1027310306
case ConstraintKind::OneWayEqual:
10307+
case ConstraintKind::OneWayBindParam:
1027410308
return simplifyOneWayConstraint(constraint.getKind(),
1027510309
constraint.getFirstType(),
1027610310
constraint.getSecondType(),

lib/Sema/CSSolver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) {
16881688
case ConstraintKind::ConformsTo:
16891689
case ConstraintKind::Defaultable:
16901690
case ConstraintKind::OneWayEqual:
1691+
case ConstraintKind::OneWayBindParam:
16911692
case ConstraintKind::DefaultClosureType:
16921693
break;
16931694
}

lib/Sema/Constraint.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second,
6666
case ConstraintKind::FunctionResult:
6767
case ConstraintKind::OpaqueUnderlyingType:
6868
case ConstraintKind::OneWayEqual:
69+
case ConstraintKind::OneWayBindParam:
6970
assert(!First.isNull());
7071
assert(!Second.isNull());
7172
break;
@@ -138,6 +139,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third,
138139
case ConstraintKind::FunctionResult:
139140
case ConstraintKind::OpaqueUnderlyingType:
140141
case ConstraintKind::OneWayEqual:
142+
case ConstraintKind::OneWayBindParam:
141143
case ConstraintKind::DefaultClosureType:
142144
llvm_unreachable("Wrong constructor");
143145

@@ -265,6 +267,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const {
265267
case ConstraintKind::FunctionResult:
266268
case ConstraintKind::OpaqueUnderlyingType:
267269
case ConstraintKind::OneWayEqual:
270+
case ConstraintKind::OneWayBindParam:
268271
case ConstraintKind::DefaultClosureType:
269272
return create(cs, getKind(), getFirstType(), getSecondType(), getLocator());
270273

@@ -348,6 +351,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const {
348351
case ConstraintKind::EscapableFunctionOf: Out << " @escaping type of "; break;
349352
case ConstraintKind::OpenedExistentialOf: Out << " opened archetype of "; break;
350353
case ConstraintKind::OneWayEqual: Out << " one-way bind to "; break;
354+
case ConstraintKind::OneWayBindParam: Out << " one-way bind param to "; break;
351355
case ConstraintKind::DefaultClosureType:
352356
Out << " closure can default to ";
353357
break;
@@ -564,6 +568,7 @@ gatherReferencedTypeVars(Constraint *constraint,
564568
case ConstraintKind::FunctionResult:
565569
case ConstraintKind::OpaqueUnderlyingType:
566570
case ConstraintKind::OneWayEqual:
571+
case ConstraintKind::OneWayBindParam:
567572
case ConstraintKind::DefaultClosureType:
568573
constraint->getFirstType()->getTypeVariables(typeVars);
569574
constraint->getSecondType()->getTypeVariables(typeVars);

lib/Sema/Constraint.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ enum class ConstraintKind : char {
161161
/// type). At that point, this constraint will be treated like an `Equal`
162162
/// constraint.
163163
OneWayEqual,
164+
/// The second type is the type of a function parameter, and the first type
165+
/// is the type of a reference to that function parameter within the body.
166+
/// Once the second type has been fully determined (and mapped down to a
167+
/// concrete type), this constraint will be treated like a 'BindParam'
168+
/// constraint.
169+
OneWayBindParam,
164170
/// If there is no contextual info e.g. `_ = { 42 }` default first type
165171
/// to a second type (inferred closure type). This is effectively a
166172
/// `Defaultable` constraint which a couple of differences:
@@ -549,6 +555,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
549555
case ConstraintKind::OptionalObject:
550556
case ConstraintKind::OpaqueUnderlyingType:
551557
case ConstraintKind::OneWayEqual:
558+
case ConstraintKind::OneWayBindParam:
552559
case ConstraintKind::DefaultClosureType:
553560
return ConstraintClassification::Relational;
554561

@@ -669,7 +676,8 @@ class Constraint final : public llvm::ilist_node<Constraint>,
669676

670677
/// Whether this is a one-way constraint.
671678
bool isOneWayConstraint() const {
672-
return Kind == ConstraintKind::OneWayEqual;
679+
return Kind == ConstraintKind::OneWayEqual ||
680+
Kind == ConstraintKind::OneWayBindParam;
673681
}
674682

675683
/// Retrieve the overload choice for an overload-binding constraint.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4 -experimental-one-way-closure-params
2+
3+
func testBasic() {
4+
let _: (Float) -> Float = { $0 + 1 }
5+
6+
let _ = { $0 + 1 } // expected-error{{unable to infer type of a closure parameter $0 in the current context}}
7+
}
8+

0 commit comments

Comments
 (0)