Skip to content

Commit de4b63d

Browse files
committed
Sema: Refactor typeCheckCheckedCast() a bit
This removes all calls to typesSatisfyConstraint() except for the isConvertibleTo() check at the beginning, in the process making the analysis a little bit more accurate.
1 parent 18c05a5 commit de4b63d

File tree

5 files changed

+52
-64
lines changed

5 files changed

+52
-64
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7168,6 +7168,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
71687168
// T1 <c T2 && T2 : Hashable ===> T1 <c AnyHashable
71697169
case ConversionRestrictionKind::HashableToAnyHashable: {
71707170
// We never want to do this if the LHS is already AnyHashable.
7171+
type1 = simplifyType(type1);
71717172
if (isAnyHashableType(
71727173
type1->getRValueType()->lookThroughAllOptionalTypes())) {
71737174
return SolutionKind::Error;

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -728,8 +728,8 @@ Optional<Type> ConstraintSystem::isSetType(Type type) {
728728
}
729729

730730
bool ConstraintSystem::isCollectionType(Type type) {
731-
auto &ctx = type->getASTContext();
732731
if (auto *structType = type->getAs<BoundGenericStructType>()) {
732+
auto &ctx = type->getASTContext();
733733
auto *decl = structType->getDecl();
734734
if (decl == ctx.getArrayDecl() || decl == ctx.getDictionaryDecl() ||
735735
decl == ctx.getSetDecl())
@@ -740,13 +740,9 @@ bool ConstraintSystem::isCollectionType(Type type) {
740740
}
741741

742742
bool ConstraintSystem::isAnyHashableType(Type type) {
743-
if (auto tv = type->getAs<TypeVariableType>()) {
744-
auto fixedType = getFixedType(tv);
745-
return fixedType && isAnyHashableType(fixedType);
746-
}
747-
748743
if (auto st = type->getAs<StructType>()) {
749-
return st->getDecl() == TC.Context.getAnyHashableDecl();
744+
auto &ctx = type->getASTContext();
745+
return st->getDecl() == ctx.getAnyHashableDecl();
750746
}
751747

752748
return false;

lib/Sema/ConstraintSystem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2491,7 +2491,7 @@ class ConstraintSystem {
24912491
static bool isCollectionType(Type t);
24922492

24932493
/// Determine if the type in question is AnyHashable.
2494-
bool isAnyHashableType(Type t);
2494+
static bool isAnyHashableType(Type t);
24952495

24962496
/// Call Expr::isTypeReference on the given expression, using a
24972497
/// custom accessor for the type on the expression that reads the

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4041,10 +4041,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
40414041
}
40424042

40434043
// Check for casts between specific concrete types that cannot succeed.
4044-
ConstraintSystem cs(*this, dc, ConstraintSystemOptions());
4045-
4046-
if (auto toElementType = cs.isArrayType(toType)) {
4047-
if (auto fromElementType = cs.isArrayType(fromType)) {
4044+
if (auto toElementType = ConstraintSystem::isArrayType(toType)) {
4045+
if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) {
40484046
switch (typeCheckCheckedCast(*fromElementType, *toElementType,
40494047
CheckedCastContextKind::None, dc,
40504048
SourceLoc(), nullptr, SourceRange())) {
@@ -4066,8 +4064,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
40664064
}
40674065
}
40684066

4069-
if (auto toKeyValue = cs.isDictionaryType(toType)) {
4070-
if (auto fromKeyValue = cs.isDictionaryType(fromType)) {
4067+
if (auto toKeyValue = ConstraintSystem::isDictionaryType(toType)) {
4068+
if (auto fromKeyValue = ConstraintSystem::isDictionaryType(fromType)) {
40714069
bool hasCoercion = false;
40724070
enum { NoBridging, BridgingCoercion }
40734071
hasBridgingConversion = NoBridging;
@@ -4130,8 +4128,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
41304128
}
41314129
}
41324130

4133-
if (auto toElementType = cs.isSetType(toType)) {
4134-
if (auto fromElementType = cs.isSetType(fromType)) {
4131+
if (auto toElementType = ConstraintSystem::isSetType(toType)) {
4132+
if (auto fromElementType = ConstraintSystem::isSetType(fromType)) {
41354133
switch (typeCheckCheckedCast(*fromElementType, *toElementType,
41364134
CheckedCastContextKind::None, dc,
41374135
SourceLoc(), nullptr, SourceRange())) {
@@ -4254,9 +4252,9 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
42544252
bool toExistentialMetatype = false;
42554253
bool fromExistentialMetatype = false;
42564254
if (auto toMetatype = toType->getAs<AnyMetatypeType>()) {
4257-
toExistentialMetatype = toMetatype->is<ExistentialMetatypeType>();
42584255
if (auto fromMetatype = fromType->getAs<AnyMetatypeType>()) {
4259-
fromExistentialMetatype = fromMetatype->is<ExistentialMetatypeType>();
4256+
toExistentialMetatype = toType->is<ExistentialMetatypeType>();
4257+
fromExistentialMetatype = fromType->is<ExistentialMetatypeType>();
42604258
toType = toMetatype->getInstanceType();
42614259
fromType = fromMetatype->getInstanceType();
42624260
}
@@ -4266,23 +4264,43 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
42664264
bool fromArchetype = fromType->is<ArchetypeType>();
42674265
bool toExistential = toType->isExistentialType();
42684266
bool fromExistential = fromType->isExistentialType();
4267+
4268+
bool toRequiresClass;
4269+
if (toType->isExistentialType())
4270+
toRequiresClass = toType->getExistentialLayout().requiresClass();
4271+
else
4272+
toRequiresClass = toType->mayHaveSuperclass();
4273+
4274+
bool fromRequiresClass;
4275+
if (fromType->isExistentialType())
4276+
fromRequiresClass = fromType->getExistentialLayout().requiresClass();
4277+
else
4278+
fromRequiresClass = fromType->mayHaveSuperclass();
42694279

4270-
// If we're doing a metatype cast, it can only be existential if we're
4271-
// casting to/from the existential metatype. 'T.self as P.Protocol'
4272-
// can only succeed if T is exactly the type P, so is a concrete cast,
4273-
// whereas 'T.self as P.Type' succeeds for types conforming to the protocol
4274-
// P, and is an existential cast.
4280+
// Casts between protocol metatypes only succeed if the type is existential.
42754281
if (metatypeCast) {
4276-
toExistential &= toExistentialMetatype;
4277-
fromExistential &= fromExistentialMetatype;
4282+
if (toExistential || fromExistential)
4283+
return failed();
42784284
}
42794285

4286+
// Casts from an existential metatype to a protocol metatype always fail,
4287+
// except when the existential type is 'Any'.
4288+
if (fromExistentialMetatype &&
4289+
!fromType->isAny() &&
4290+
!toExistentialMetatype &&
4291+
toExistential)
4292+
return failed();
4293+
42804294
// Casts to or from generic types can't be statically constrained in most
42814295
// cases, because there may be protocol conformances we don't statically
42824296
// know about.
4283-
if (toExistential || fromExistential || fromArchetype || toArchetype) {
4297+
if (toExistential || fromExistential || fromArchetype || toArchetype ||
4298+
toRequiresClass || fromRequiresClass) {
42844299
// Cast to and from AnyObject always succeed.
4285-
if (toType->isAnyObject() || fromType->isAnyObject())
4300+
if (!metatypeCast &&
4301+
!fromExistentialMetatype &&
4302+
!toExistentialMetatype &&
4303+
(toType->isAnyObject() || fromType->isAnyObject()))
42864304
return CheckedCastKind::ValueCast;
42874305

42884306
// A cast from an existential type to a concrete type does not succeed. For
@@ -4316,18 +4334,6 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
43164334
}
43174335
}
43184336

4319-
bool toRequiresClass;
4320-
if (toType->isExistentialType())
4321-
toRequiresClass = toType->getExistentialLayout().requiresClass();
4322-
else
4323-
toRequiresClass = toType->mayHaveSuperclass();
4324-
4325-
bool fromRequiresClass;
4326-
if (fromType->isExistentialType())
4327-
fromRequiresClass = fromType->getExistentialLayout().requiresClass();
4328-
else
4329-
fromRequiresClass = fromType->mayHaveSuperclass();
4330-
43314337
// If neither type is class-constrained, anything goes.
43324338
if (!fromRequiresClass && !toRequiresClass)
43334339
return CheckedCastKind::ValueCast;
@@ -4373,44 +4379,29 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
43734379
return CheckedCastKind::ValueCast;
43744380

43754381
// Compare superclass bounds.
4376-
if (typesSatisfyConstraint(toSuperclass, fromSuperclass,
4377-
/*openArchetypes=*/true,
4378-
ConstraintKind::Subtype, dc))
4382+
if (fromSuperclass->isBindableToSuperclassOf(toSuperclass))
43794383
return CheckedCastKind::ValueCast;
43804384

43814385
// An upcast is also OK.
4382-
if (typesSatisfyConstraint(fromSuperclass, toSuperclass,
4383-
/*openArchetypes=*/true,
4384-
ConstraintKind::Subtype, dc))
4386+
if (toSuperclass->isBindableToSuperclassOf(fromSuperclass))
43854387
return CheckedCastKind::ValueCast;
43864388
}
43874389
}
43884390

4389-
if (cs.isAnyHashableType(toType) || cs.isAnyHashableType(fromType)) {
4391+
if (ConstraintSystem::isAnyHashableType(toType) ||
4392+
ConstraintSystem::isAnyHashableType(fromType)) {
43904393
return CheckedCastKind::ValueCast;
43914394
}
43924395

43934396
// If the destination type can be a supertype of the source type, we are
43944397
// performing what looks like an upcast except it rebinds generic
43954398
// parameters.
4396-
if (!metatypeCast &&
4397-
typesSatisfyConstraint(fromType, toType,
4398-
/*openArchetypes=*/true,
4399-
ConstraintKind::Subtype, dc)) {
4399+
if (fromType->isBindableTo(toType))
44004400
return CheckedCastKind::ValueCast;
4401-
}
4402-
4403-
// If the destination type can be a subtype of the source type, we have
4404-
// a downcast.
4405-
if (typesSatisfyConstraint(toType, fromType,
4406-
/*openArchetypes=*/true,
4407-
ConstraintKind::Subtype, dc)) {
4408-
return CheckedCastKind::ValueCast;
4409-
}
44104401

44114402
// Objective-C metaclasses are subclasses of NSObject in the ObjC runtime,
44124403
// so casts from NSObject to potentially-class metatypes may succeed.
4413-
if (auto nsObject = cs.TC.getNSObjectType(dc)) {
4404+
if (auto nsObject = getNSObjectType(dc)) {
44144405
if (fromType->isEqual(nsObject)) {
44154406
if (auto toMeta = toType->getAs<MetatypeType>()) {
44164407
if (toMeta->getInstanceType()->mayHaveSuperclass()

test/expr/cast/metatype_casts.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ use(any as! P.Protocol)
2323

2424
let anyP: Any.Protocol = Any.self
2525
use(anyP is Any.Type) // expected-warning{{always true}}
26-
use(anyP as! Int.Type) // TODO: always fails
26+
use(anyP as! Int.Type) // expected-warning{{always fails}}
2727

2828
let anyObj: AnyObject.Type = D.self
29-
use(anyObj as! Int.Type) // TODO: always fails
29+
use(anyObj as! Int.Type) // expected-warning{{always fails}}
3030
use(anyObj as! C.Type)
3131
use(anyObj as! D.Type)
32-
use(anyObj as! AnyObject.Protocol) // TODO: always fails
32+
use(anyObj as! AnyObject.Protocol) // expected-warning{{always fails}}
3333
use(anyObj as! P.Type)
34-
use(anyObj as! P.Protocol) // TODO: always fails
34+
use(anyObj as! P.Protocol) // expected-warning{{always fails}}
3535

3636
let c: C.Type = D.self
3737
use(c as! D.Type)

0 commit comments

Comments
 (0)