Skip to content

Commit 1e1b342

Browse files
committed
Experimental support for implicitly opening existential arguments.
When calling a generic function with an argument of existential type, implicitly "open" the existential type into a concrete archetype, which can then be bound to the generic type. This extends the implicit opening that is performed when accessing a member of an existential type from the "self" parameter to all parameters. For example: func unsafeFirst<C: Collection>(_ c: C) -> C.Element { c.first! } func g(c: any Collection) { unsafeFirst(c) // currently an error // with this change, succeeds and produces an 'Any' } This avoids many common sources of errors of the form protocol 'P' as a type cannot conform to the protocol itself which come from calling generic functions with an existential, and allows another way "out" if one has an existention and needs to treat it generically. This feature is behind a frontend flag `-enable-experimental-opened-existential-types`.
1 parent b3b8fa5 commit 1e1b342

File tree

8 files changed

+209
-58
lines changed

8 files changed

+209
-58
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ namespace swift {
313313
/// `func f() -> <T> T`.
314314
bool EnableExperimentalNamedOpaqueTypes = false;
315315

316+
/// Enable support for implicitly opening existential argument types
317+
/// in calls to generic functions.
318+
bool EnableOpenedExistentialTypes = false;
319+
316320
/// Enable support for protocol types parameterized by primary
317321
/// associated type.
318322
bool EnableParameterizedProtocolTypes = false;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,10 @@ def enable_parameterized_protocol_types :
514514
Flag<["-"], "enable-parameterized-protocol-types">,
515515
HelpText<"Enable experimental support for primary associated types and parameterized protocols">;
516516

517+
def enable_experimental_opened_existential_types :
518+
Flag<["-"], "enable-experimental-opened-existential-types">,
519+
HelpText<"Enable experimental support for implicitly opened existentials">;
520+
517521
def enable_deserialization_recovery :
518522
Flag<["-"], "enable-deserialization-recovery">,
519523
HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;

include/swift/Sema/ConstraintSystem.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,6 +3353,11 @@ class ConstraintSystem {
33533353
ConstraintLocator *getOpenOpaqueLocator(
33543354
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl);
33553355

3356+
/// Open the given existential type, recording the opened type in the
3357+
/// constraint system and returning both it and the root opened archetype.
3358+
std::pair<Type, OpenedArchetypeType *> openExistentialType(
3359+
Type type, ConstraintLocator *locator);
3360+
33563361
/// Retrive the constraint locator for the given anchor and
33573362
/// path, uniqued and automatically infer the summary flags
33583363
ConstraintLocator *
@@ -5506,20 +5511,16 @@ matchCallArguments(
55065511
MatchCallArgumentListener &listener,
55075512
Optional<TrailingClosureMatching> trailingClosureMatching);
55085513

5509-
ConstraintSystem::TypeMatchResult
5510-
matchCallArguments(ConstraintSystem &cs,
5511-
FunctionType *contextualType,
5512-
ArgumentList *argumentList,
5513-
ArrayRef<AnyFunctionType::Param> args,
5514-
ArrayRef<AnyFunctionType::Param> params,
5515-
ConstraintKind subKind,
5516-
ConstraintLocatorBuilder locator,
5517-
Optional<TrailingClosureMatching> trailingClosureMatching);
5518-
55195514
/// Given an expression that is the target of argument labels (for a call,
55205515
/// subscript, etc.), find the underlying target expression.
55215516
Expr *getArgumentLabelTargetExpr(Expr *fn);
55225517

5518+
/// Given a type that includes an existential type that has been opened to
5519+
/// the given type variable, type-erase occurences of that opened type
5520+
/// variable and anything that depends on it to their non-dependent bounds.
5521+
Type typeEraseOpenedExistentialReference(Type type, Type existentialBaseType,
5522+
TypeVariableType *openedTypeVar);
5523+
55235524
/// Returns true if a reference to a member on a given base type will apply
55245525
/// its curried self parameter, assuming it has one.
55255526
///

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
448448
Opts.EnableParameterizedProtocolTypes |=
449449
Args.hasArg(OPT_enable_parameterized_protocol_types);
450450

451+
Opts.EnableOpenedExistentialTypes |=
452+
Args.hasArg(OPT_enable_experimental_opened_existential_types);
453+
451454
Opts.EnableExperimentalDistributed |=
452455
Args.hasArg(OPT_enable_experimental_distributed);
453456

lib/Sema/CSApply.cpp

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,9 +1141,9 @@ namespace {
11411141
return archetypeVal;
11421142
}
11431143

1144-
/// Trying to close the active existential, if there is one.
1144+
/// Try to close the innermost active existential, if there is one.
11451145
bool closeExistential(Expr *&result, ConstraintLocatorBuilder locator,
1146-
bool force=false) {
1146+
bool force) {
11471147
if (OpenedExistentials.empty())
11481148
return false;
11491149

@@ -1185,6 +1185,18 @@ namespace {
11851185
return true;
11861186
}
11871187

1188+
/// Close any active existentials.
1189+
bool closeExistentials(Expr *&result, ConstraintLocatorBuilder locator,
1190+
bool force=false) {
1191+
bool closedAny = false;
1192+
while (closeExistential(result, locator, force)) {
1193+
force = false;
1194+
closedAny = true;
1195+
}
1196+
1197+
return closedAny;
1198+
}
1199+
11881200
/// Determines if a partially-applied member reference should be
11891201
/// converted into a fully-applied member reference with a pair of
11901202
/// closures.
@@ -1706,7 +1718,7 @@ namespace {
17061718

17071719
cs.setType(ref, refType);
17081720

1709-
closeExistential(ref, locator, /*force=*/openedExistential);
1721+
closeExistentials(ref, locator, /*force=*/openedExistential);
17101722

17111723
// If this attribute was inferred based on deprecated Swift 3 rules,
17121724
// complain.
@@ -1758,7 +1770,7 @@ namespace {
17581770
cs.setType(memberRefExpr, refTy->castTo<FunctionType>()->getResult());
17591771

17601772
Expr *result = memberRefExpr;
1761-
closeExistential(result, locator);
1773+
closeExistentials(result, locator);
17621774

17631775
// If the property is of dynamic 'Self' type, wrap an implicit
17641776
// conversion around the resulting expression, with the destination
@@ -1959,7 +1971,7 @@ namespace {
19591971
ref,
19601972
cs.getType(ref));
19611973
cs.cacheType(result);
1962-
closeExistential(result, locator, /*force=*/openedExistential);
1974+
closeExistentials(result, locator, /*force=*/openedExistential);
19631975
return forceUnwrapIfExpected(result, memberLocator);
19641976
} else {
19651977
assert((!baseIsInstance || member->isInstanceMember()) &&
@@ -2263,7 +2275,7 @@ namespace {
22632275
&& "open existential archetype in AnyObject subscript type?");
22642276
cs.setType(subscriptExpr, resultTy);
22652277
Expr *result = subscriptExpr;
2266-
closeExistential(result, locator);
2278+
closeExistentials(result, locator);
22672279
return result;
22682280
}
22692281

@@ -2305,7 +2317,7 @@ namespace {
23052317
: fullSubscriptTy->getResult());
23062318

23072319
Expr *result = subscriptExpr;
2308-
closeExistential(result, locator);
2320+
closeExistentials(result, locator);
23092321

23102322
// If the element is of dynamic 'Self' type, wrap an implicit conversion
23112323
// around the resulting expression, with the destination type having
@@ -6029,6 +6041,22 @@ ArgumentList *ExprRewriter::coerceCallArguments(
60296041
cs.cacheExprTypes(argExpr);
60306042
}
60316043

6044+
auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());
6045+
6046+
// If the argument is an existential type that has been opened, perform
6047+
// the open operation.
6048+
if (argType->isAnyExistentialType() && paramType->hasOpenedExistential()) {
6049+
// FIXME: Look for an opened existential and use it. We need to
6050+
// know how far out we need to go to close the existentials. Huh.
6051+
auto knownOpened = solution.OpenedExistentialTypes.find(
6052+
cs.getConstraintLocator(argLoc));
6053+
if (knownOpened != solution.OpenedExistentialTypes.end()) {
6054+
argExpr = openExistentialReference(
6055+
argExpr, knownOpened->second, callee.getDecl());
6056+
argType = cs.getType(argExpr);
6057+
}
6058+
}
6059+
60326060
if (argRequiresAutoClosureExpr(param, argType)) {
60336061
assert(!param.isInOut());
60346062

@@ -6038,8 +6066,6 @@ ArgumentList *ExprRewriter::coerceCallArguments(
60386066
// - new types are propagated to constraint system
60396067
auto *closureType = param.getPlainType()->castTo<FunctionType>();
60406068

6041-
auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());
6042-
60436069
argExpr = coerceToType(
60446070
argExpr, closureType->getResult(),
60456071
argLoc.withPathElement(ConstraintLocator::AutoclosureResult));
@@ -6062,9 +6088,7 @@ ArgumentList *ExprRewriter::coerceCallArguments(
60626088

60636089
convertedArg = cs.buildAutoClosureExpr(argExpr, closureType, dc);
60646090
} else {
6065-
convertedArg = coerceToType(
6066-
argExpr, paramType,
6067-
getArgLocator(argIdx, paramIdx, param.getParameterFlags()));
6091+
convertedArg = coerceToType(argExpr, paramType, argLoc);
60686092
}
60696093

60706094
// Perform the wrapped value placeholder injection
@@ -7800,8 +7824,8 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
78007824
result, covariantResultType));
78017825
}
78027826

7803-
// Try closing the existential, if there is one.
7804-
closeExistential(result, locator);
7827+
// Try closing existentials, if there are any.
7828+
closeExistentials(result, locator);
78057829

78067830
// We may also need to force the result for an IUO. We don't apply this on
78077831
// SelfApplyExprs, as the force unwraps should be inserted at the result of

lib/Sema/CSSimplify.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,13 +1319,17 @@ class OpenTypeSequenceElements {
13191319
}
13201320

13211321
// Match the argument of a call to the parameter.
1322-
ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
1322+
static ConstraintSystem::TypeMatchResult matchCallArguments(
13231323
ConstraintSystem &cs, FunctionType *contextualType,
13241324
ArgumentList *argList,
13251325
ArrayRef<AnyFunctionType::Param> args,
13261326
ArrayRef<AnyFunctionType::Param> params, ConstraintKind subKind,
13271327
ConstraintLocatorBuilder locator,
1328-
Optional<TrailingClosureMatching> trailingClosureMatching) {
1328+
Optional<TrailingClosureMatching> trailingClosureMatching,
1329+
SmallVectorImpl<std::pair<TypeVariableType *, OpenedArchetypeType *>>
1330+
&openedExistentials) {
1331+
assert(subKind == ConstraintKind::OperatorArgumentConversion ||
1332+
subKind == ConstraintKind::ArgumentConversion);
13291333
auto *loc = cs.getConstraintLocator(locator);
13301334
assert(loc->isLastElement<LocatorPathElt::ApplyArgument>());
13311335

@@ -1546,6 +1550,27 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
15461550
cs, cs.getConstraintLocator(loc)));
15471551
}
15481552

1553+
// If the argument is an existential type and the parameter is generic,
1554+
// consider opening the existential type.
1555+
if (argTy->isAnyExistentialType() &&
1556+
cs.getASTContext().LangOpts.EnableOpenedExistentialTypes &&
1557+
paramTy->hasTypeVariable() &&
1558+
!(callee &&
1559+
TypeChecker::getDeclTypeCheckingSemantics(callee) ==
1560+
DeclTypeCheckingSemantics::OpenExistential)) {
1561+
if (auto param = getParameterAt(callee, argIdx)) {
1562+
if (auto paramTypeVar = paramTy->getAs<TypeVariableType>()) {
1563+
if (param->getInterfaceType()->is<GenericTypeParamType>()) {
1564+
OpenedArchetypeType *opened;
1565+
std::tie(argTy, opened) = cs.openExistentialType(
1566+
argTy, cs.getConstraintLocator(loc));
1567+
1568+
openedExistentials.push_back({paramTypeVar, opened});
1569+
}
1570+
}
1571+
}
1572+
}
1573+
15491574
auto argLabel = argument.getLabel();
15501575
if (paramInfo.hasExternalPropertyWrapper(argIdx) || argLabel.hasDollarPrefix()) {
15511576
auto *param = getParameterAt(callee, argIdx);
@@ -10686,9 +10711,11 @@ ConstraintSystem::simplifyApplicableFnConstraint(
1068610711

1068710712
auto *argumentList = getArgumentList(argumentsLoc);
1068810713
// The argument type must be convertible to the input type.
10714+
SmallVector<std::pair<TypeVariableType *, OpenedArchetypeType *>, 2>
10715+
openedExistentials;
1068910716
auto matchCallResult = ::matchCallArguments(
1069010717
*this, func2, argumentList, func1->getParams(), func2->getParams(),
10691-
subKind, argumentsLoc, trailingClosureMatching);
10718+
subKind, argumentsLoc, trailingClosureMatching, openedExistentials);
1069210719

1069310720
switch (matchCallResult) {
1069410721
case SolutionKind::Error: {
@@ -10743,7 +10770,8 @@ ConstraintSystem::simplifyApplicableFnConstraint(
1074310770

1074410771
auto matchCallResult = ::matchCallArguments(
1074510772
*this, func2, newArgumentList, func1->getParams(),
10746-
func2->getParams(), subKind, argumentsLoc, trailingClosureMatching);
10773+
func2->getParams(), subKind, argumentsLoc, trailingClosureMatching,
10774+
openedExistentials);
1074710775

1074810776
if (matchCallResult != SolutionKind::Solved)
1074910777
return SolutionKind::Error;
@@ -10796,9 +10824,18 @@ ConstraintSystem::simplifyApplicableFnConstraint(
1079610824
break;
1079710825
}
1079810826

10827+
// Erase all of the opened existentials.
10828+
Type result2 = func2->getResult();
10829+
if (result2->hasTypeVariable() && !openedExistentials.empty()) {
10830+
for (const auto &opened : openedExistentials) {
10831+
result2 = typeEraseOpenedExistentialReference(
10832+
result2, opened.second->getExistentialType(), opened.first);
10833+
}
10834+
}
10835+
1079910836
// The result types are equivalent.
1080010837
if (matchFunctionResultTypes(
10801-
func1->getResult(), func2->getResult(), subflags,
10838+
func1->getResult(), result2, subflags,
1080210839
locator.withPathElement(ConstraintLocator::FunctionResult))
1080310840
.isFailure())
1080410841
return SolutionKind::Error;

lib/Sema/ConstraintSystem.cpp

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,15 @@ ConstraintLocator *ConstraintSystem::getOpenOpaqueLocator(
573573
{ LocatorPathElt::OpenedOpaqueArchetype(opaqueDecl) }, 0);
574574
}
575575

576+
std::pair<Type, OpenedArchetypeType *> ConstraintSystem::openExistentialType(
577+
Type type, ConstraintLocator *locator) {
578+
OpenedArchetypeType *opened = nullptr;
579+
Type result = type->openAnyExistentialType(opened);
580+
assert(OpenedExistentialTypes.count(locator) == 0);
581+
OpenedExistentialTypes.insert({locator, opened});
582+
return {result, opened};
583+
}
584+
576585
/// Extend the given depth map by adding depths for all of the subexpressions
577586
/// of the given expression.
578587
static void extendDepthMap(
@@ -1847,6 +1856,40 @@ static Type typeEraseCovariantExistentialSelfReferences(Type refTy,
18471856
return transformFn(refTy, TypePosition::Covariant);
18481857
}
18491858

1859+
Type constraints::typeEraseOpenedExistentialReference(
1860+
Type type, Type existentialBaseType, TypeVariableType *openedTypeVar) {
1861+
Type selfGP = GenericTypeParamType::get(false, 0, 0, type->getASTContext());
1862+
1863+
// First, temporarily reconstitute the 'Self' generic parameter.
1864+
type = type.transformRec([&](TypeBase *t) -> Optional<Type> {
1865+
// Don't recurse into children unless we have to.
1866+
if (!type->hasTypeVariable())
1867+
return Type(t);
1868+
1869+
if (isa<TypeVariableType>(t) && t->isEqual(openedTypeVar))
1870+
return selfGP;
1871+
1872+
// Recurse.
1873+
return None;
1874+
});
1875+
1876+
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
1877+
type = typeEraseCovariantExistentialSelfReferences(type, existentialBaseType);
1878+
1879+
// Finally, swap the 'Self'-corresponding type variable back in.
1880+
return type.transformRec([&](TypeBase *t) -> Optional<Type> {
1881+
// Don't recurse into children unless we have to.
1882+
if (!type->hasTypeParameter())
1883+
return Type(t);
1884+
1885+
if (isa<GenericTypeParamType>(t) && t->isEqual(selfGP))
1886+
return Type(openedTypeVar);
1887+
1888+
// Recurse.
1889+
return None;
1890+
});
1891+
}
1892+
18501893
std::pair<Type, Type>
18511894
ConstraintSystem::getTypeOfMemberReference(
18521895
Type baseTy, ValueDecl *value, DeclContext *useDC,
@@ -2119,35 +2162,8 @@ ConstraintSystem::getTypeOfMemberReference(
21192162
type->hasTypeVariable()) {
21202163
const auto selfGP = cast<GenericTypeParamType>(
21212164
outerDC->getSelfInterfaceType()->getCanonicalType());
2122-
2123-
// First, temporarily reconstitute the 'Self' generic parameter.
2124-
type = type.transformRec([&](TypeBase *t) -> Optional<Type> {
2125-
// Don't recurse into children unless we have to.
2126-
if (!type->hasTypeVariable())
2127-
return Type(t);
2128-
2129-
if (isa<TypeVariableType>(t) && t->isEqual(replacements.lookup(selfGP)))
2130-
return selfGP;
2131-
2132-
// Recurse.
2133-
return None;
2134-
});
2135-
2136-
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
2137-
type = typeEraseCovariantExistentialSelfReferences(type, baseObjTy);
2138-
2139-
// Finally, swap the 'Self'-corresponding type variable back in.
2140-
type = type.transformRec([&](TypeBase *t) -> Optional<Type> {
2141-
// Don't recurse into children unless we have to.
2142-
if (!type->hasTypeParameter())
2143-
return Type(t);
2144-
2145-
if (isa<GenericTypeParamType>(t) && t->isEqual(selfGP))
2146-
return Type(replacements.lookup(selfGP));
2147-
2148-
// Recurse.
2149-
return None;
2150-
});
2165+
auto openedTypeVar = replacements.lookup(selfGP);
2166+
type = typeEraseOpenedExistentialReference(type, baseObjTy, openedTypeVar);
21512167
}
21522168

21532169
// Construct an idealized parameter type of the initializer associated

0 commit comments

Comments
 (0)