diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 9e4a053e972d4..b3f6bb2047259 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -549,7 +549,10 @@ namespace swift { /// Enable constraint solver support for experimental /// operator protocol designator feature. bool SolverEnableOperatorDesignatedTypes = false; - + + /// Enable experimental support for one-way constraints for the + /// parameters of closures. + bool EnableOneWayClosureParameters = false; }; } // end namespace swift diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 9ba2fc6ed4fe8..42b4e5db2974c 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -642,6 +642,10 @@ def experimental_print_full_convention : HelpText<"When emitting a module interface, emit additional @convention " "arguments, regardless of whether they were written in the source">; +def experimental_one_way_closure_params : + Flag<["-"], "experimental-one-way-closure-params">, + HelpText<"Enable experimental support for one-way closure parameters">; + def prebuilt_module_cache_path : Separate<["-"], "prebuilt-module-cache-path">, HelpText<"Directory of prebuilt modules for loading module interfaces">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9675923a8d675..f3a78f9373765 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -724,6 +724,8 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args, Opts.SolverEnableOperatorDesignatedTypes |= Args.hasArg(OPT_solver_enable_operator_designated_types); + Opts.EnableOneWayClosureParameters |= + Args.hasArg(OPT_experimental_one_way_closure_params); Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 76f5586f2d147..a05f865768a32 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -710,7 +710,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { } break; - case ConstraintKind::OneWayEqual: { + case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: { // Don't produce any bindings if this type variable is on the left-hand // side of a one-way binding. auto firstType = constraint->getFirstType(); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index b52cb39d501a3..6c9a75fa68c27 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1322,6 +1322,7 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: llvm_unreachable("Not a conversion"); } @@ -1387,6 +1388,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1, case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: return false; } @@ -1699,6 +1701,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: llvm_unreachable("Not a relational constraint"); } @@ -4406,6 +4409,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: llvm_unreachable("Not a relational constraint"); } @@ -7121,9 +7125,16 @@ ConstraintSystem::simplifyOneWayConstraint( return SolutionKind::Solved; } - // Translate this constraint into a one-way binding constraint. - return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, - locator); + // Translate this constraint into an equality or bind-parameter constraint, + // as appropriate. + if (kind == ConstraintKind::OneWayEqual) { + return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, + locator); + } + + assert(kind == ConstraintKind::OneWayBindParam); + return matchTypes( + secondSimplified, first, ConstraintKind::BindParam, flags, locator); } static Type getFunctionBuilderTypeFor(ConstraintSystem &cs, unsigned paramIdx, @@ -7175,12 +7186,27 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, Type internalType; + bool oneWayConstraints = + getASTContext().TypeCheckerOpts.EnableOneWayClosureParameters; if (paramList->get(i)->getTypeRepr()) { // Internal type is the type used in the body of the closure, // so "external" type translates to it as follows: // - `Int...` -> `[Int]`, // - `inout Int` -> `@lvalue Int`. internalType = param.getParameterType(); + + // When there are type variables in the type and we have enabled + // one-way constraints, create a fresh type variable to handle the + // binding. + if (oneWayConstraints && internalType->hasTypeVariable()) { + auto *paramLoc = + getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); + auto *typeVar = createTypeVariable(paramLoc, TVO_CanBindToLValue | + TVO_CanBindToNoEscape); + addConstraint( + ConstraintKind::OneWayBindParam, typeVar, internalType, paramLoc); + internalType = typeVar; + } } else { auto *paramLoc = getConstraintLocator(closure, LocatorPathElt::TupleElement(i)); @@ -7194,7 +7220,13 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, param.isVariadic() ? ArraySliceType::get(typeVar) : Type(typeVar); auto externalType = param.getOldType(); - addConstraint(ConstraintKind::BindParam, externalType, typeVar, paramLoc); + if (oneWayConstraints) { + addConstraint( + ConstraintKind::OneWayBindParam, typeVar, externalType, paramLoc); + } else { + addConstraint( + ConstraintKind::BindParam, externalType, typeVar, paramLoc); + } } setType(paramList->get(i), internalType); @@ -9759,6 +9791,7 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, subflags, locator); case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: return simplifyOneWayConstraint(kind, first, second, subflags, locator); case ConstraintKind::ValueMember: @@ -10271,6 +10304,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { return SolutionKind::Unsolved; case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: return simplifyOneWayConstraint(constraint.getKind(), constraint.getFirstType(), constraint.getSecondType(), diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 4c8f4c977ecd4..df67727623fa2 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1688,6 +1688,7 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) { case ConstraintKind::ConformsTo: case ConstraintKind::Defaultable: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: break; } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 99e433a171380..0756b06ba7b6d 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -66,6 +66,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: assert(!First.isNull()); assert(!Second.isNull()); break; @@ -138,6 +139,7 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: llvm_unreachable("Wrong constructor"); @@ -265,6 +267,7 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: return create(cs, getKind(), getFirstType(), getSecondType(), getLocator()); @@ -348,6 +351,7 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { case ConstraintKind::EscapableFunctionOf: Out << " @escaping type of "; break; case ConstraintKind::OpenedExistentialOf: Out << " opened archetype of "; break; case ConstraintKind::OneWayEqual: Out << " one-way bind to "; break; + case ConstraintKind::OneWayBindParam: Out << " one-way bind param to "; break; case ConstraintKind::DefaultClosureType: Out << " closure can default to "; break; @@ -564,6 +568,7 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: constraint->getFirstType()->getTypeVariables(typeVars); constraint->getSecondType()->getTypeVariables(typeVars); diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 23e615fed081d..d13a95bb39889 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -161,6 +161,12 @@ enum class ConstraintKind : char { /// type). At that point, this constraint will be treated like an `Equal` /// constraint. OneWayEqual, + /// The second type is the type of a function parameter, and the first type + /// is the type of a reference to that function parameter within the body. + /// Once the second type has been fully determined (and mapped down to a + /// concrete type), this constraint will be treated like a 'BindParam' + /// constraint. + OneWayBindParam, /// If there is no contextual info e.g. `_ = { 42 }` default first type /// to a second type (inferred closure type). This is effectively a /// `Defaultable` constraint which a couple of differences: @@ -549,6 +555,7 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::OptionalObject: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: return ConstraintClassification::Relational; @@ -669,7 +676,8 @@ class Constraint final : public llvm::ilist_node, /// Whether this is a one-way constraint. bool isOneWayConstraint() const { - return Kind == ConstraintKind::OneWayEqual; + return Kind == ConstraintKind::OneWayEqual || + Kind == ConstraintKind::OneWayBindParam; } /// Retrieve the overload choice for an overload-binding constraint. diff --git a/test/Constraints/one_way_closure_params.swift b/test/Constraints/one_way_closure_params.swift new file mode 100644 index 0000000000000..c62bb7256cb7b --- /dev/null +++ b/test/Constraints/one_way_closure_params.swift @@ -0,0 +1,8 @@ +// RUN: %target-typecheck-verify-swift -swift-version 4 -experimental-one-way-closure-params + +func testBasic() { + let _: (Float) -> Float = { $0 + 1 } + + let _ = { $0 + 1 } // expected-error{{unable to infer type of a closure parameter $0 in the current context}} +} +