Skip to content

Commit 2e5b3b6

Browse files
committed
Sema: Update constraint solver for subclass existentials
1 parent 4efa9ea commit 2e5b3b6

File tree

4 files changed

+168
-48
lines changed

4 files changed

+168
-48
lines changed

lib/Sema/CSApply.cpp

Lines changed: 96 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,20 @@ namespace {
336336
int toScalarIdx,
337337
ConstraintLocatorBuilder locator);
338338

339+
/// \brief Coerce a subclass, class-constrained archetype, class-constrained
340+
/// existential or to a superclass type.
341+
///
342+
/// Also supports metatypes of the above.
343+
///
344+
/// \param expr The expression to be coerced.
345+
/// \param toType The type to which the expression will be coerced.
346+
/// \param locator Locator describing where this conversion occurs.
347+
///
348+
/// \return The coerced expression, whose type will be equivalent to
349+
/// \c toType.
350+
Expr *coerceSuperclass(Expr *expr, Type toType,
351+
ConstraintLocatorBuilder locator);
352+
339353
/// \brief Coerce the given value to existential type.
340354
///
341355
/// The following conversions are supported:
@@ -4590,6 +4604,79 @@ Expr *ExprRewriter::coerceScalarToTuple(Expr *expr, TupleType *toTuple,
45904604
destSugarTy));
45914605
}
45924606

4607+
static Type getMetatypeSuperclass(Type t, TypeChecker &tc) {
4608+
if (auto *metaTy = t->getAs<MetatypeType>())
4609+
return MetatypeType::get(getMetatypeSuperclass(
4610+
metaTy->getInstanceType(),
4611+
tc));
4612+
4613+
if (auto *metaTy = t->getAs<ExistentialMetatypeType>())
4614+
return ExistentialMetatypeType::get(getMetatypeSuperclass(
4615+
metaTy->getInstanceType(),
4616+
tc));
4617+
4618+
return tc.getSuperClassOf(t);
4619+
}
4620+
4621+
Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType,
4622+
ConstraintLocatorBuilder locator) {
4623+
auto &tc = cs.getTypeChecker();
4624+
4625+
auto fromType = cs.getType(expr);
4626+
4627+
auto fromInstanceType = fromType;
4628+
auto toInstanceType = toType;
4629+
4630+
while (fromInstanceType->is<AnyMetatypeType>() &&
4631+
toInstanceType->is<MetatypeType>()) {
4632+
fromInstanceType = fromInstanceType->castTo<AnyMetatypeType>()
4633+
->getInstanceType();
4634+
toInstanceType = toInstanceType->castTo<MetatypeType>()
4635+
->getInstanceType();
4636+
}
4637+
4638+
if (fromInstanceType->is<ArchetypeType>()) {
4639+
// Coercion from archetype to its (concrete) superclass.
4640+
auto superclass = getMetatypeSuperclass(fromType, tc);
4641+
4642+
expr =
4643+
cs.cacheType(
4644+
new (tc.Context) ArchetypeToSuperExpr(expr, superclass));
4645+
4646+
if (!superclass->isEqual(toType))
4647+
return coerceSuperclass(expr, toType, locator);
4648+
4649+
return expr;
4650+
4651+
}
4652+
4653+
if (fromInstanceType->isExistentialType()) {
4654+
// Coercion from superclass-constrained existential to its
4655+
// concrete superclass.
4656+
auto fromArchetype = ArchetypeType::getAnyOpened(fromType);
4657+
4658+
auto *archetypeVal =
4659+
cs.cacheType(
4660+
new (tc.Context) OpaqueValueExpr(expr->getLoc(),
4661+
fromArchetype));
4662+
4663+
auto *result = coerceSuperclass(archetypeVal, toType, locator);
4664+
4665+
return cs.cacheType(
4666+
new (tc.Context) OpenExistentialExpr(expr, archetypeVal, result,
4667+
toType));
4668+
}
4669+
4670+
// Coercion from subclass to superclass.
4671+
if (toType->is<MetatypeType>()) {
4672+
return cs.cacheType(
4673+
new (tc.Context) MetatypeConversionExpr(expr, toType));
4674+
}
4675+
4676+
return cs.cacheType(
4677+
new (tc.Context) DerivedToBaseExpr(expr, toType));
4678+
}
4679+
45934680
/// Collect the conformances for all the protocols of an existential type.
45944681
/// If the source type is also existential, we don't want to check conformance
45954682
/// because most protocols do not conform to themselves -- however we still
@@ -5453,25 +5540,8 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
54535540
assert(toType->hasUnresolvedType() && "Should have handled this above");
54545541
break;
54555542

5456-
case ConversionRestrictionKind::Superclass: {
5457-
// Coercion from archetype to its (concrete) superclass.
5458-
if (auto fromArchetype = fromType->getAs<ArchetypeType>()) {
5459-
expr =
5460-
cs.cacheType(
5461-
new (tc.Context) ArchetypeToSuperExpr(expr,
5462-
fromArchetype->getSuperclass()));
5463-
5464-
// If we are done succeeded, use the coerced result.
5465-
if (cs.getType(expr)->isEqual(toType)) {
5466-
return expr;
5467-
}
5468-
5469-
fromType = cs.getType(expr);
5470-
}
5471-
5472-
// Coercion from subclass to superclass.
5473-
return cs.cacheType(new (tc.Context) DerivedToBaseExpr(expr, toType));
5474-
}
5543+
case ConversionRestrictionKind::Superclass:
5544+
return coerceSuperclass(expr, toType, locator);
54755545

54765546
case ConversionRestrictionKind::LValueToRValue: {
54775547
if (toType->is<TupleType>() || fromType->is<TupleType>())
@@ -5490,6 +5560,9 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
54905560
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
54915561
return coerceExistential(expr, toType, locator);
54925562

5563+
case ConversionRestrictionKind::ExistentialMetatypeToMetatype:
5564+
return coerceSuperclass(expr, toType, locator);
5565+
54935566
case ConversionRestrictionKind::ClassMetatypeToAnyObject: {
54945567
assert(tc.getLangOpts().EnableObjCInterop
54955568
&& "metatypes can only be cast to objects w/ objc runtime!");
@@ -5752,25 +5825,16 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
57525825
}
57535826

57545827
// Coercion from a subclass to a superclass.
5828+
//
5829+
// FIXME: Can we rig things up so that we always have a Superclass
5830+
// conversion restriction in this case?
57555831
if (fromType->mayHaveSuperclass() &&
57565832
toType->getClassOrBoundGenericClass()) {
57575833
for (auto fromSuperClass = tc.getSuperClassOf(fromType);
57585834
fromSuperClass;
57595835
fromSuperClass = tc.getSuperClassOf(fromSuperClass)) {
57605836
if (fromSuperClass->isEqual(toType)) {
5761-
5762-
// Coercion from archetype to its (concrete) superclass.
5763-
if (auto fromArchetype = fromType->getAs<ArchetypeType>()) {
5764-
expr = cs.cacheType(new (tc.Context) ArchetypeToSuperExpr(
5765-
expr, fromArchetype->getSuperclass()));
5766-
5767-
// If we succeeded, use the coerced result.
5768-
if (cs.getType(expr)->isEqual(toType))
5769-
return expr;
5770-
}
5771-
5772-
// Coercion from subclass to superclass.
5773-
return cs.cacheType(new (tc.Context) DerivedToBaseExpr(expr, toType));
5837+
return coerceSuperclass(expr, toType, locator);
57745838
}
57755839
}
57765840
}

lib/Sema/CSSimplify.cpp

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "ConstraintSystem.h"
19+
#include "swift/AST/ExistentialLayout.h"
1920
#include "swift/AST/ParameterList.h"
2021
#include "swift/Basic/StringExtras.h"
2122
#include "swift/ClangImporter/ClangModule.h"
@@ -1180,11 +1181,28 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
11801181
if (!type2->isExistentialType())
11811182
return SolutionKind::Error;
11821183

1183-
SmallVector<ProtocolDecl *, 4> protocols;
1184-
type2->getExistentialTypeProtocols(protocols);
1184+
auto layout = type2->getExistentialLayout();
11851185

1186-
for (auto proto : protocols) {
1187-
switch (simplifyConformsToConstraint(type1, proto, kind, locator,
1186+
assert((!layout.requiresClass || layout.requiresClassImplied) &&
1187+
"explicit AnyObject not yet supported");
1188+
1189+
if (layout.superclass) {
1190+
llvm::errs() << "superclass case\n";
1191+
auto subKind = std::min(ConstraintKind::Subtype, kind);
1192+
switch (matchTypes(type1, layout.superclass, subKind, subflags, locator)) {
1193+
case SolutionKind::Solved:
1194+
case SolutionKind::Unsolved:
1195+
break;
1196+
1197+
case SolutionKind::Error:
1198+
return SolutionKind::Error;
1199+
}
1200+
}
1201+
1202+
for (auto *proto : layout.getProtocols()) {
1203+
auto *protoDecl = proto->getDecl();
1204+
1205+
switch (simplifyConformsToConstraint(type1, protoDecl, kind, locator,
11881206
subflags)) {
11891207
case SolutionKind::Solved:
11901208
case SolutionKind::Unsolved:
@@ -1809,14 +1827,14 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
18091827
}
18101828

18111829
// Subclass-to-superclass conversion.
1812-
if (type1->mayHaveSuperclass() && type2->mayHaveSuperclass() &&
1830+
if (type1->mayHaveSuperclass() &&
1831+
type2->mayHaveSuperclass() &&
18131832
type2->getClassOrBoundGenericClass() &&
18141833
type1->getClassOrBoundGenericClass()
18151834
!= type2->getClassOrBoundGenericClass()) {
1816-
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
18171835
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
18181836
}
1819-
1837+
18201838
// T -> AnyHashable.
18211839
if (isAnyHashableType(desugar2)) {
18221840
// Don't allow this in operator contexts or we'll end up allowing
@@ -2059,18 +2077,31 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
20592077
// equivalent to a conformance relationship on the instance types.
20602078
// This applies to nested metatype levels, so if A : P then
20612079
// A.Type : P.Type.
2062-
if (concrete && kind >= ConstraintKind::Subtype &&
2063-
type1->is<MetatypeType>() && type2->is<ExistentialMetatypeType>()) {
2080+
if (concrete &&
2081+
kind >= ConstraintKind::Subtype &&
2082+
type1->is<MetatypeType>() &&
2083+
type2->is<ExistentialMetatypeType>()) {
20642084
conversionsOrFixes.push_back(
20652085
ConversionRestrictionKind::MetatypeToExistentialMetatype);
20662086
}
2067-
2087+
2088+
// An existential metatype can be upcast to a class metatype if the
2089+
// existential contains a superclass constraint.
2090+
if (concrete &&
2091+
kind >= ConstraintKind::Subtype &&
2092+
type1->is<ExistentialMetatypeType>() &&
2093+
type2->is<MetatypeType>()) {
2094+
conversionsOrFixes.push_back(
2095+
ConversionRestrictionKind::ExistentialMetatypeToMetatype);
2096+
}
2097+
20682098
// Instance type check for the above. We are going to check conformance once
20692099
// we hit commit_to_conversions below, but we have to add a token restriction
20702100
// to ensure we wrap the metatype value in a metatype erasure.
2071-
if (concrete && type2->isExistentialType() &&
2101+
if (concrete &&
2102+
kind >= ConstraintKind::Subtype &&
20722103
!type1->is<LValueType>() &&
2073-
kind >= ConstraintKind::Subtype) {
2104+
type2->isExistentialType()) {
20742105

20752106
// Penalize conversions to Any.
20762107
if (kind >= ConstraintKind::Conversion && type2->isAny())
@@ -3893,10 +3924,10 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
38933924
subflags, locator);
38943925

38953926
// for $< in { <, <c, <oc }:
3896-
// for T protocol,
3897-
// T : S ===> T.Protocol $< S.Type
3898-
// else,
3899-
// T : S ===> T.Type $< S.Type
3927+
// for P protocol, Q protocol,
3928+
// P : Q ===> T.Protocol $< Q.Type
3929+
// for P protocol, Q protocol,
3930+
// P $< Q ===> P.Type $< Q.Type
39003931
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
39013932
addContextualScore();
39023933

@@ -3907,6 +3938,27 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
39073938
subflags,
39083939
locator.withPathElement(ConstraintLocator::InstanceType));
39093940

3941+
// for $< in { <, <c, <oc }:
3942+
// for P protocol, C class, D class,
3943+
// (P & C) : D ===> (P & C).Type $< D.Type
3944+
case ConversionRestrictionKind::ExistentialMetatypeToMetatype: {
3945+
addContextualScore();
3946+
3947+
auto instance1 = type1->castTo<ExistentialMetatypeType>()->getInstanceType();
3948+
auto instance2 = type2->castTo<MetatypeType>()->getInstanceType();
3949+
auto superclass1 = TC.getSuperClassOf(instance1);
3950+
3951+
if (!superclass1)
3952+
return SolutionKind::Error;
3953+
3954+
return matchTypes(
3955+
superclass1,
3956+
instance2,
3957+
ConstraintKind::Subtype,
3958+
subflags,
3959+
locator.withPathElement(ConstraintLocator::InstanceType));
3960+
3961+
}
39103962
// for $< in { <, <c, <oc }:
39113963
// T $< U ===> T $< U?
39123964
case ConversionRestrictionKind::ValueToOptional: {

lib/Sema/Constraint.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) {
371371
return "[existential]";
372372
case ConversionRestrictionKind::MetatypeToExistentialMetatype:
373373
return "[metatype-to-existential-metatype]";
374+
case ConversionRestrictionKind::ExistentialMetatypeToMetatype:
375+
return "[existential-metatype-to-metatype]";
374376
case ConversionRestrictionKind::ValueToOptional:
375377
return "[value-to-optional]";
376378
case ConversionRestrictionKind::OptionalToOptional:

lib/Sema/Constraint.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ enum class ConversionRestrictionKind {
187187
Existential,
188188
/// Metatype to existential metatype conversion.
189189
MetatypeToExistentialMetatype,
190+
/// Existential metatype to metatype conversion.
191+
ExistentialMetatypeToMetatype,
190192
/// T -> U? value to optional conversion (or to implicitly unwrapped optional).
191193
ValueToOptional,
192194
/// T? -> U? optional to optional conversion (or unchecked to unchecked).

0 commit comments

Comments
 (0)