Skip to content

Commit 3e13ca6

Browse files
authored
Merge pull request #32323 from DougGregor/function-builder-one-way-params-5.3
[5.3] [Function builders] Use one-way constraints for closure parameters.
2 parents 991cc3f + fd29659 commit 3e13ca6

File tree

11 files changed

+117
-29
lines changed

11 files changed

+117
-29
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: 60 additions & 16 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,
@@ -7104,6 +7115,24 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71047115
auto *closure = cast<ClosureExpr>(closureLocator->getAnchor());
71057116
auto *inferredClosureType = getClosureType(closure);
71067117

7118+
// Determine whether a function builder will be applied.
7119+
Type functionBuilderType;
7120+
ConstraintLocator *calleeLocator = nullptr;
7121+
if (auto last = locator.last()) {
7122+
if (auto argToParam = last->getAs<LocatorPathElt::ApplyArgToParam>()) {
7123+
calleeLocator = getCalleeLocator(getConstraintLocator(locator));
7124+
functionBuilderType = getFunctionBuilderTypeFor(
7125+
*this, argToParam->getParamIdx(), calleeLocator);
7126+
}
7127+
}
7128+
7129+
// Determine whether to introduce one-way constraints between the parameter's
7130+
// type as seen in the body of the closure and the external parameter
7131+
// type.
7132+
bool oneWayConstraints =
7133+
getASTContext().TypeCheckerOpts.EnableOneWayClosureParameters ||
7134+
functionBuilderType;
7135+
71077136
auto *paramList = closure->getParameters();
71087137
SmallVector<AnyFunctionType::Param, 4> parameters;
71097138
for (unsigned i = 0, n = paramList->size(); i != n; ++i) {
@@ -7117,13 +7146,25 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71177146
}
71187147

71197148
Type internalType;
7120-
71217149
if (paramList->get(i)->getTypeRepr()) {
71227150
// Internal type is the type used in the body of the closure,
71237151
// so "external" type translates to it as follows:
71247152
// - `Int...` -> `[Int]`,
71257153
// - `inout Int` -> `@lvalue Int`.
71267154
internalType = param.getParameterType();
7155+
7156+
// When there are type variables in the type and we have enabled
7157+
// one-way constraints, create a fresh type variable to handle the
7158+
// binding.
7159+
if (oneWayConstraints && internalType->hasTypeVariable()) {
7160+
auto *paramLoc =
7161+
getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
7162+
auto *typeVar = createTypeVariable(paramLoc, TVO_CanBindToLValue |
7163+
TVO_CanBindToNoEscape);
7164+
addConstraint(
7165+
ConstraintKind::OneWayBindParam, typeVar, internalType, paramLoc);
7166+
internalType = typeVar;
7167+
}
71277168
} else {
71287169
auto *paramLoc =
71297170
getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
@@ -7137,7 +7178,13 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71377178
param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar);
71387179

71397180
auto externalType = param.getOldType();
7140-
addConstraint(ConstraintKind::BindParam, externalType, typeVar, paramLoc);
7181+
if (oneWayConstraints) {
7182+
addConstraint(
7183+
ConstraintKind::OneWayBindParam, typeVar, externalType, paramLoc);
7184+
} else {
7185+
addConstraint(
7186+
ConstraintKind::BindParam, externalType, typeVar, paramLoc);
7187+
}
71417188
}
71427189

71437190
setType(paramList->get(i), internalType);
@@ -7149,17 +7196,12 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
71497196
inferredClosureType->getExtInfo());
71507197
assignFixedType(typeVar, closureType, closureLocator);
71517198

7152-
if (auto last = locator.last()) {
7153-
if (auto argToParam = last->getAs<LocatorPathElt::ApplyArgToParam>()) {
7154-
auto *calleeLocator = getCalleeLocator(getConstraintLocator(locator));
7155-
if (auto functionBuilderType = getFunctionBuilderTypeFor(
7156-
*this, argToParam->getParamIdx(), calleeLocator)) {
7157-
if (auto result = matchFunctionBuilder(
7158-
closure, functionBuilderType, closureType->getResult(),
7159-
ConstraintKind::Conversion, calleeLocator, locator)) {
7160-
return result->isSuccess();
7161-
}
7162-
}
7199+
// If there is a function builder to apply, do so now.
7200+
if (functionBuilderType) {
7201+
if (auto result = matchFunctionBuilder(
7202+
closure, functionBuilderType, closureType->getResult(),
7203+
ConstraintKind::Conversion, calleeLocator, locator)) {
7204+
return result->isSuccess();
71637205
}
71647206
}
71657207

@@ -9687,6 +9729,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
96879729
subflags, locator);
96889730

96899731
case ConstraintKind::OneWayEqual:
9732+
case ConstraintKind::OneWayBindParam:
96909733
return simplifyOneWayConstraint(kind, first, second, subflags, locator);
96919734

96929735
case ConstraintKind::ValueMember:
@@ -10194,6 +10237,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
1019410237
return SolutionKind::Unsolved;
1019510238

1019610239
case ConstraintKind::OneWayEqual:
10240+
case ConstraintKind::OneWayBindParam:
1019710241
return simplifyOneWayConstraint(constraint.getKind(),
1019810242
constraint.getFirstType(),
1019910243
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.

test/Constraints/function_builder_diags.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,3 +558,14 @@ func rdar61347993() {
558558
func test_closure(_: () -> Result) {}
559559
test_closure {} // expected-error {{cannot convert value of type '()' to closure result type 'Result'}}
560560
}
561+
562+
// One-way constraints through parameters.
563+
func wrapperifyInfer<T, U>(_ cond: Bool, @WrapperBuilder body: (U) -> T) -> T {
564+
fatalError("boom")
565+
}
566+
567+
let intValue = 17
568+
wrapperifyInfer(true) { x in // expected-error{{unable to infer type of a closure parameter 'x' in the current context}}
569+
intValue + x
570+
}
571+

test/Constraints/function_builder_one_way.swift

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@ func tuplify<C: Collection, T>(_ collection: C, @TupleBuilder body: (C.Element)
4949
}
5050

5151
// CHECK: ---Connected components---
52-
// CHECK-NEXT: 0: $T1 $T2 $T3 $T5 $T6 $T7 $T8 $T10 $T77 $T78 depends on 2
53-
// CHECK-NEXT: 2: $T12 $T17 $T28 $T42 $T53 $T54 $T55 $T56 $T57 $T58 $T59 $T60 $T61 $T62 $T63 $T64 $T65 $T66 $T68 $T69 $T70 $T71 $T72 $T73 $T74 $T75 $T76 depends on 1, 3, 4, 6, 9
54-
// CHECK-NEXT: 9: $T48 $T49 $T50 $T51 $T52 depends on 8
55-
// CHECK-NEXT: 8: $T43 $T44 $T45 $T46 $T47
56-
// CHECK-NEXT: 6: $T31 $T35 $T36 $T37 $T38 $T39 $T40 $T41 depends on 5, 7
57-
// CHECK-NEXT: 7: $T32 $T33 $T34
58-
// CHECK-NEXT: 5: $T30
59-
// CHECK-NEXT: 4: $T18 $T19 $T20 $T21 $T22 $T23 $T24 $T25 $T26 $T27
60-
// CHECK-NEXT: 3: $T15 $T16
61-
// CHECK-NEXT: 1: $T11
52+
// CHECK-NEXT: 1: $T10 depends on 0
53+
// CHECK-NEXT: 0: $T1 $T2 $T3 $T5 $T6 $T7 $T8 $T77 $T78 depends on 3
54+
// CHECK-NEXT: 3: $T12 $T17 $T28 $T42 $T53 $T54 $T55 $T56 $T57 $T58 $T59 $T60 $T61 $T62 $T63 $T64 $T65 $T66 $T68 $T69 $T70 $T71 $T72 $T73 $T74 $T75 $T76 depends on 2, 4, 5, 7, 10
55+
// CHECK-NEXT: 10: $T48 $T49 $T50 $T51 $T52 depends on 9
56+
// CHECK-NEXT: 9: $T43 $T44 $T45 $T46 $T47
57+
// CHECK-NEXT: 7: $T31 $T35 $T36 $T37 $T38 $T39 $T40 $T41 depends on 6, 8
58+
// CHECK-NEXT: 8: $T32 $T33 $T34
59+
// CHECK-NEXT: 6: $T30
60+
// CHECK-NEXT: 5: $T18 $T19 $T20 $T21 $T22 $T23 $T24 $T25 $T26 $T27
61+
// CHECK-NEXT: 4: $T15 $T16
62+
// CHECK-NEXT: 2: $T11
6263
let names = ["Alice", "Bob", "Charlie"]
6364
let b = true
6465
var number = 17
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)