Skip to content

Commit b556c73

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. (cherry picked from commit 64f903f)
1 parent eaa3e34 commit b556c73

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
@@ -540,7 +540,10 @@ namespace swift {
540540
/// Enable constraint solver support for experimental
541541
/// operator protocol designator feature.
542542
bool SolverEnableOperatorDesignatedTypes = false;
543-
543+
544+
/// Enable experimental support for one-way constraints for the
545+
/// parameters of closures.
546+
bool EnableOneWayClosureParameters = false;
544547
};
545548
} // end namespace swift
546549

include/swift/Option/FrontendOptions.td

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

635+
def experimental_one_way_closure_params :
636+
Flag<["-"], "experimental-one-way-closure-params">,
637+
HelpText<"Enable experimental support for one-way closure parameters">;
638+
635639
def prebuilt_module_cache_path :
636640
Separate<["-"], "prebuilt-module-cache-path">,
637641
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
@@ -690,6 +690,8 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
690690

691691
Opts.SolverEnableOperatorDesignatedTypes |=
692692
Args.hasArg(OPT_solver_enable_operator_designated_types);
693+
Opts.EnableOneWayClosureParameters |=
694+
Args.hasArg(OPT_experimental_one_way_closure_params);
693695

694696
Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints);
695697
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
@@ -711,7 +711,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const {
711711
}
712712
break;
713713

714-
case ConstraintKind::OneWayEqual: {
714+
case ConstraintKind::OneWayEqual:
715+
case ConstraintKind::OneWayBindParam: {
715716
// Don't produce any bindings if this type variable is on the left-hand
716717
// side of a one-way binding.
717718
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
}
@@ -1698,6 +1700,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
16981700
case ConstraintKind::FunctionInput:
16991701
case ConstraintKind::FunctionResult:
17001702
case ConstraintKind::OneWayEqual:
1703+
case ConstraintKind::OneWayBindParam:
17011704
case ConstraintKind::DefaultClosureType:
17021705
llvm_unreachable("Not a relational constraint");
17031706
}
@@ -4340,6 +4343,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
43404343
case ConstraintKind::FunctionInput:
43414344
case ConstraintKind::FunctionResult:
43424345
case ConstraintKind::OneWayEqual:
4346+
case ConstraintKind::OneWayBindParam:
43434347
case ConstraintKind::DefaultClosureType:
43444348
llvm_unreachable("Not a relational constraint");
43454349
}
@@ -7064,9 +7068,16 @@ ConstraintSystem::simplifyOneWayConstraint(
70647068
return SolutionKind::Solved;
70657069
}
70667070

7067-
// Translate this constraint into a one-way binding constraint.
7068-
return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags,
7069-
locator);
7071+
// Translate this constraint into an equality or bind-parameter constraint,
7072+
// as appropriate.
7073+
if (kind == ConstraintKind::OneWayEqual) {
7074+
return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags,
7075+
locator);
7076+
}
7077+
7078+
assert(kind == ConstraintKind::OneWayBindParam);
7079+
return matchTypes(
7080+
secondSimplified, first, ConstraintKind::BindParam, flags, locator);
70707081
}
70717082

70727083
static Type getFunctionBuilderTypeFor(ConstraintSystem &cs, unsigned paramIdx,
@@ -7118,12 +7129,27 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71187129

71197130
Type internalType;
71207131

7132+
bool oneWayConstraints =
7133+
getASTContext().TypeCheckerOpts.EnableOneWayClosureParameters;
71217134
if (paramList->get(i)->getTypeRepr()) {
71227135
// Internal type is the type used in the body of the closure,
71237136
// so "external" type translates to it as follows:
71247137
// - `Int...` -> `[Int]`,
71257138
// - `inout Int` -> `@lvalue Int`.
71267139
internalType = param.getParameterType();
7140+
7141+
// When there are type variables in the type and we have enabled
7142+
// one-way constraints, create a fresh type variable to handle the
7143+
// binding.
7144+
if (oneWayConstraints && internalType->hasTypeVariable()) {
7145+
auto *paramLoc =
7146+
getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
7147+
auto *typeVar = createTypeVariable(paramLoc, TVO_CanBindToLValue |
7148+
TVO_CanBindToNoEscape);
7149+
addConstraint(
7150+
ConstraintKind::OneWayBindParam, typeVar, internalType, paramLoc);
7151+
internalType = typeVar;
7152+
}
71277153
} else {
71287154
auto *paramLoc =
71297155
getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
@@ -7137,7 +7163,13 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71377163
param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar);
71387164

71397165
auto externalType = param.getOldType();
7140-
addConstraint(ConstraintKind::BindParam, externalType, typeVar, paramLoc);
7166+
if (oneWayConstraints) {
7167+
addConstraint(
7168+
ConstraintKind::OneWayBindParam, typeVar, externalType, paramLoc);
7169+
} else {
7170+
addConstraint(
7171+
ConstraintKind::BindParam, externalType, typeVar, paramLoc);
7172+
}
71417173
}
71427174

71437175
setType(paramList->get(i), internalType);
@@ -9687,6 +9719,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
96879719
subflags, locator);
96889720

96899721
case ConstraintKind::OneWayEqual:
9722+
case ConstraintKind::OneWayBindParam:
96909723
return simplifyOneWayConstraint(kind, first, second, subflags, locator);
96919724

96929725
case ConstraintKind::ValueMember:
@@ -10194,6 +10227,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
1019410227
return SolutionKind::Unsolved;
1019510228

1019610229
case ConstraintKind::OneWayEqual:
10230+
case ConstraintKind::OneWayBindParam:
1019710231
return simplifyOneWayConstraint(constraint.getKind(),
1019810232
constraint.getFirstType(),
1019910233
constraint.getSecondType(),

lib/Sema/CSSolver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) {
17031703
case ConstraintKind::ConformsTo:
17041704
case ConstraintKind::Defaultable:
17051705
case ConstraintKind::OneWayEqual:
1706+
case ConstraintKind::OneWayBindParam:
17061707
case ConstraintKind::DefaultClosureType:
17071708
break;
17081709
}

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)