Skip to content

Commit 28fb66c

Browse files
[SR-13088] Fix false positive downcast unrelated of types that cannot be statically known (swiftlang#32592)
* [TypeCheckConstraints] Adjusting cases where checked casts that cannot be determined statically were producing misleading warnings * [tests] Adding regression tests for SR-13088 * [TypeCheckConstraints] Adjusting comment and adding an extra test case for SR13035 * [TypeCheckConstraints] Fixing typos in comments * [AST] Moving implementation of isCollection from ConstraintSystem to AST TypeBase * [TypeCheckConstraints] Adjusting logic to verify specific conformance to stdlib collection type before emit an downcast warning * [TypeCheckConstraints] Creating new CheckedCastContextKind::CollectionElement to be able to verify special cases within typeCheckCheckedCast for collection elements * [TypeCheckConstraints] Adjusting logic around generic substitution to check both subtype and supertype * [Sema] Adding isKnownStdlibCollectionType and replacing all usages contraint system method * [TypeChecker] Reverting fixes around array element types * [TypeChecker] Abstract logic of check for conditional requirements on TypeChecker::couldDynamicallyConformToProtocol * [TypeChecker] Ajdustinc can conformDynamically conform and adjust review comments * [TypeChecker] Ajusting comments and fixing typos * [TypeChecker] Adjusting existential and archetype logic to check inside couldDynamicConform * [TypeChecker] Adjusting minor and adding existential check into couldDynamically conform. * [TypeChecker] Adjusting comments
1 parent f88fd50 commit 28fb66c

File tree

12 files changed

+155
-47
lines changed

12 files changed

+155
-47
lines changed

include/swift/AST/Types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,10 @@ class alignas(1 << TypeAlignInBits) TypeBase {
785785

786786
/// Check if this is a nominal type defined at the top level of the Swift module
787787
bool isStdlibType();
788+
789+
/// Check if this is either an Array, Set or Dictionary collection type defined
790+
/// at the top level of the Swift module
791+
bool isKnownStdlibCollectionType();
788792

789793
/// If this is a class type or a bound generic class type, returns the
790794
/// (possibly generic) class.

lib/AST/Type.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,16 @@ bool TypeBase::isStdlibType() {
760760
return false;
761761
}
762762

763+
bool TypeBase::isKnownStdlibCollectionType() {
764+
if (auto *structType = getAs<BoundGenericStructType>()) {
765+
auto &ctx = getASTContext();
766+
auto *decl = structType->getDecl();
767+
return decl == ctx.getArrayDecl() || decl == ctx.getDictionaryDecl() ||
768+
decl == ctx.getSetDecl();
769+
}
770+
return false;
771+
}
772+
763773
/// Remove argument labels from the function type.
764774
Type TypeBase::removeArgumentLabels(unsigned numArgumentLabels) {
765775
// If there is nothing to remove, don't.

lib/Sema/CSBindings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,7 @@ bool TypeVarBindingProducer::computeNext() {
10351035
auto srcLocator = binding.getLocator();
10361036
if (srcLocator &&
10371037
srcLocator->isLastElement<LocatorPathElt::ApplyArgToParam>() &&
1038-
!type->hasTypeVariable() && CS.isCollectionType(type)) {
1038+
!type->hasTypeVariable() && type->isKnownStdlibCollectionType()) {
10391039
// If the type binding comes from the argument conversion, let's
10401040
// instead of binding collection types directly, try to bind
10411041
// using temporary type variables substituted for element

lib/Sema/CSDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3376,7 +3376,7 @@ bool MissingMemberFailure::diagnoseInLiteralCollectionContext() const {
33763376

33773377
auto parentType = getType(parentExpr);
33783378

3379-
if (!isCollectionType(parentType) && !parentType->is<TupleType>())
3379+
if (!parentType->isKnownStdlibCollectionType() && !parentType->is<TupleType>())
33803380
return false;
33813381

33823382
if (isa<TupleExpr>(parentExpr)) {

lib/Sema/CSDiagnostics.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,6 @@ class FailureDiagnostic {
208208
llvm::function_ref<void(GenericTypeParamType *, Type)> substitution =
209209
[](GenericTypeParamType *, Type) {});
210210

211-
bool isCollectionType(Type type) const {
212-
auto &cs = getConstraintSystem();
213-
return cs.isCollectionType(type);
214-
}
215-
216211
bool isArrayType(Type type) const {
217212
auto &cs = getConstraintSystem();
218213
return bool(cs.isArrayType(type));

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3708,7 +3708,7 @@ bool ConstraintSystem::repairFailures(
37083708
// func foo<T>(_: [T]) {}
37093709
// foo(1) // expected '[Int]', got 'Int'
37103710
// ```
3711-
if (isCollectionType(rhs)) {
3711+
if (rhs->isKnownStdlibCollectionType()) {
37123712
std::function<Type(Type)> getArrayOrSetType = [&](Type type) -> Type {
37133713
if (auto eltTy = isArrayType(type))
37143714
return getArrayOrSetType(*eltTy);

lib/Sema/ConstraintSystem.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -878,18 +878,6 @@ Optional<Type> ConstraintSystem::isSetType(Type type) {
878878
return None;
879879
}
880880

881-
bool ConstraintSystem::isCollectionType(Type type) {
882-
if (auto *structType = type->getAs<BoundGenericStructType>()) {
883-
auto &ctx = type->getASTContext();
884-
auto *decl = structType->getDecl();
885-
if (decl == ctx.getArrayDecl() || decl == ctx.getDictionaryDecl() ||
886-
decl == ctx.getSetDecl())
887-
return true;
888-
}
889-
890-
return false;
891-
}
892-
893881
bool ConstraintSystem::isAnyHashableType(Type type) {
894882
if (auto st = type->getAs<StructType>()) {
895883
auto &ctx = type->getASTContext();

lib/Sema/ConstraintSystem.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,9 +3348,6 @@ class ConstraintSystem {
33483348
/// element type of the set.
33493349
static Optional<Type> isSetType(Type t);
33503350

3351-
/// Determine if the type in question is one of the known collection types.
3352-
static bool isCollectionType(Type t);
3353-
33543351
/// Determine if the type in question is AnyHashable.
33553352
static bool isAnyHashableType(Type t);
33563353

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,7 +3357,14 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
33573357
const auto &fromElt = fromTuple->getElement(i);
33583358
const auto &toElt = toTuple->getElement(i);
33593359

3360-
if (fromElt.getName() != toElt.getName())
3360+
// We should only perform name validation if both element have a label,
3361+
// because unlabeled tuple elements can be converted to labeled ones
3362+
// e.g.
3363+
//
3364+
// let tup: (Any, Any) = (1, 1)
3365+
// _ = tup as! (a: Int, Int)
3366+
if ((!fromElt.getName().empty() && !toElt.getName().empty()) &&
3367+
fromElt.getName() != toElt.getName())
33613368
return failed();
33623369

33633370
auto result = checkElementCast(fromElt.getType(), toElt.getType(),
@@ -3527,8 +3534,9 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
35273534
(toType->isAnyObject() || fromType->isAnyObject()))
35283535
return CheckedCastKind::ValueCast;
35293536

3530-
// A cast from an existential type to a concrete type does not succeed. For
3531-
// example:
3537+
// If we have a cast from an existential type to a concrete type that we
3538+
// statically know doesn't conform to the protocol, mark the cast as always
3539+
// failing. For example:
35323540
//
35333541
// struct S {}
35343542
// enum FooError: Error { case bar }
@@ -3541,19 +3549,10 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
35413549
// }
35423550
// }
35433551
//
3544-
// Note: we relax the restriction if the type we're casting to is a
3545-
// non-final class because it's possible that we might have a subclass
3546-
// that conforms to the protocol.
3547-
if (fromExistential && !toExistential) {
3548-
if (auto NTD = toType->getAnyNominal()) {
3549-
if (!toType->is<ClassType>() || NTD->isFinal()) {
3550-
auto protocolDecl =
3551-
dyn_cast_or_null<ProtocolDecl>(fromType->getAnyNominal());
3552-
if (protocolDecl &&
3553-
!conformsToProtocol(toType, protocolDecl, dc)) {
3554-
return failed();
3555-
}
3556-
}
3552+
if (auto *protocolDecl =
3553+
dyn_cast_or_null<ProtocolDecl>(fromType->getAnyNominal())) {
3554+
if (!couldDynamicallyConformToProtocol(toType, protocolDecl, dc)) {
3555+
return failed();
35573556
}
35583557
}
35593558

@@ -3616,10 +3615,12 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
36163615
return CheckedCastKind::ValueCast;
36173616
}
36183617

3619-
// If the destination type can be a supertype of the source type, we are
3620-
// performing what looks like an upcast except it rebinds generic
3621-
// parameters.
3622-
if (fromType->isBindableTo(toType))
3618+
// We perform an upcast while rebinding generic parameters if it's possible
3619+
// to substitute the generic arguments of the source type with the generic
3620+
// archetypes of the destination type. Or, if it's possible to substitute
3621+
// the generic arguments of the destination type with the generic archetypes
3622+
// of the source type, we perform a downcast instead.
3623+
if (toType->isBindableTo(fromType) || fromType->isBindableTo(toType))
36233624
return CheckedCastKind::ValueCast;
36243625

36253626
// Objective-C metaclasses are subclasses of NSObject in the ObjC runtime,
@@ -3636,8 +3637,8 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
36363637
}
36373638
}
36383639

3639-
// We can conditionally cast from NSError to an Error-conforming
3640-
// type. This is handled in the runtime, so it doesn't need a special cast
3640+
// We can conditionally cast from NSError to an Error-conforming type.
3641+
// This is handled in the runtime, so it doesn't need a special cast
36413642
// kind.
36423643
if (Context.LangOpts.EnableObjCInterop) {
36433644
auto nsObject = Context.getNSObjectType();

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4527,6 +4527,40 @@ TypeChecker::conformsToProtocol(Type T, ProtocolDecl *Proto, DeclContext *DC,
45274527
return lookupResult;
45284528
}
45294529

4530+
bool
4531+
TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto,
4532+
DeclContext *DC) {
4533+
// An existential may have a concrete underlying type with protocol conformances
4534+
// we cannot know statically.
4535+
if (type->isExistentialType())
4536+
return true;
4537+
4538+
// A generic archetype may have protocol conformances we cannot know
4539+
// statically.
4540+
if (type->is<ArchetypeType>())
4541+
return true;
4542+
4543+
// A non-final class might have a subclass that conforms to the protocol.
4544+
if (auto *classDecl = type->getClassOrBoundGenericClass()) {
4545+
if (!classDecl->isFinal())
4546+
return true;
4547+
}
4548+
4549+
ModuleDecl *M = DC->getParentModule();
4550+
// For standard library collection types such as Array, Set or Dictionary
4551+
// which have custom casting machinery implemented in situations like:
4552+
//
4553+
// func encodable(_ value: Encodable) {
4554+
// _ = value as! [String : Encodable]
4555+
// }
4556+
// we are skipping checking conditional requirements using lookupConformance,
4557+
// as an intermediate collection cast can dynamically change if the conditions
4558+
// are met or not.
4559+
if (type->isKnownStdlibCollectionType())
4560+
return !M->lookupConformance(type, Proto).isInvalid();
4561+
return !conformsToProtocol(type, Proto, DC).isInvalid();
4562+
}
4563+
45304564
/// Exposes TypeChecker functionality for querying protocol conformance.
45314565
/// Returns a valid ProtocolConformanceRef only if all conditional
45324566
/// requirements are successfully resolved.

0 commit comments

Comments
 (0)