Skip to content

[Experiment] One-way closure parameters #32215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
42 changes: 38 additions & 4 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/Constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion lib/Sema/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -549,6 +555,7 @@ class Constraint final : public llvm::ilist_node<Constraint>,
case ConstraintKind::OptionalObject:
case ConstraintKind::OpaqueUnderlyingType:
case ConstraintKind::OneWayEqual:
case ConstraintKind::OneWayBindParam:
case ConstraintKind::DefaultClosureType:
return ConstraintClassification::Relational;

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

/// 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.
Expand Down
8 changes: 8 additions & 0 deletions test/Constraints/one_way_closure_params.swift
Original file line number Diff line number Diff line change
@@ -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}}
}