Skip to content

Commit 92da766

Browse files
committed
[Type checker] Validate class constraints for generic arguments of types.
Class constraints (spelled T: AnyObject) on generic types were not getting checked on generic arguments. This appears to be a regression introduced in Swift 4.0 with the removal of AnyObject, leading to a fairly significant soundness hole that could produce crashers later on. Fixes SR-6841 / rdar://problem/36884025. (cherry picked from commit ee99c8a)
1 parent 6550687 commit 92da766

File tree

10 files changed

+65
-28
lines changed

10 files changed

+65
-28
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,10 @@ NOTE(candidate_types_inheritance_requirement,none,
14051405
(Type, Type, Type, Type, StringRef))
14061406
NOTE(types_not_equal_requirement,none,
14071407
"requirement specified as %0 == %1%2", (Type, Type, StringRef))
1408+
ERROR(type_is_not_a_class,none,
1409+
"%0 requires that %1 be a class type", (Type, Type, Type))
1410+
NOTE(anyobject_requirement,none,
1411+
"requirement specified as %0 : 'AnyObject'%2", (Type, Type, StringRef))
14081412
ERROR(non_class_cannot_conform_to_class_protocol,none,
14091413
"non-class type %0 cannot conform to class protocol %1",
14101414
(Type, Type))

include/swift/AST/Types.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,16 @@ class alignas(1 << TypeAlignInBits) TypeBase {
670670
/// with a class type.
671671
bool mayHaveSuperclass();
672672

673+
/// Determine whether this type satisfies a class layout constraint, written
674+
/// `T: AnyObject` in the source.
675+
///
676+
/// A class layout constraint is satisfied when we have a single retainable
677+
/// pointer as the representation, which includes:
678+
/// - @objc existentials
679+
/// - class constrained archetypes
680+
/// - classes
681+
bool satisfiesClassConstraint();
682+
673683
/// \brief Determine whether this type can be used as a base type for AST
674684
/// name lookup, which is the case for nominal types, protocol compositions
675685
/// and archetypes.

lib/AST/Type.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,10 @@ bool TypeBase::mayHaveSuperclass() {
14451445
return is<DynamicSelfType>();
14461446
}
14471447

1448+
bool TypeBase::satisfiesClassConstraint() {
1449+
return mayHaveSuperclass() || isObjCExistentialType();
1450+
}
1451+
14481452
Type TypeBase::getSuperclass() {
14491453
auto *nominalDecl = getAnyNominal();
14501454
auto *classDecl = dyn_cast_or_null<ClassDecl>(nominalDecl);

lib/SIL/TypeLowering.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,10 +2290,8 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2) {
22902290
// Classes, class-constrained archetypes, and pure-ObjC existential types
22912291
// all have single retainable pointer representation; optionality change
22922292
// is allowed.
2293-
if ((type1.getSwiftRValueType()->mayHaveSuperclass() ||
2294-
type1.getSwiftRValueType()->isObjCExistentialType()) &&
2295-
(type2.getSwiftRValueType()->mayHaveSuperclass() ||
2296-
type2.getSwiftRValueType()->isObjCExistentialType()))
2293+
if (type1.getSwiftRValueType()->satisfiesClassConstraint() &&
2294+
type2.getSwiftRValueType()->satisfiesClassConstraint())
22972295
return ABIDifference::Trivial;
22982296

22992297
// Function parameters are ABI compatible if their differences are

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,14 +1329,7 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
13291329
if (auto layoutConstraint = layout.getLayoutConstraint()) {
13301330
if (layoutConstraint->isClass()) {
13311331
if (kind == ConstraintKind::ConformsTo) {
1332-
// Conformance to AnyObject is defined by having a single
1333-
// retainable pointer representation:
1334-
//
1335-
// - @objc existentials
1336-
// - class constrained archetypes
1337-
// - classes
1338-
if (!type1->isObjCExistentialType() &&
1339-
!type1->mayHaveSuperclass())
1332+
if (!type1->satisfiesClassConstraint())
13401333
return SolutionKind::Error;
13411334
} else {
13421335
// Subtype relation to AnyObject also allows class-bound

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,9 +2827,7 @@ bool TypeChecker::checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc) {
28272827

28282828
bool TypeChecker::isSubstitutableFor(Type type, ArchetypeType *archetype,
28292829
DeclContext *dc) {
2830-
if (archetype->requiresClass() &&
2831-
!type->mayHaveSuperclass() &&
2832-
!type->isObjCExistentialType())
2830+
if (archetype->requiresClass() && !type->satisfiesClassConstraint())
28332831
return false;
28342832

28352833
if (auto superclass = archetype->getSuperclass()) {

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@ TypeChecker::gatherGenericParamBindingsText(
366366
TypeSubstitutionFn substitutions) {
367367
llvm::SmallPtrSet<GenericTypeParamType *, 2> knownGenericParams;
368368
for (auto type : types) {
369+
if (type.isNull()) continue;
370+
369371
type.visit([&](Type type) {
370372
if (auto gp = type->getAs<GenericTypeParamType>()) {
371373
knownGenericParams.insert(
@@ -1373,12 +1375,16 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13731375
break;
13741376
}
13751377

1376-
case RequirementKind::Layout: {
1377-
// TODO: Statically check if a the first type
1378-
// conforms to the layout constraint, once we
1379-
// support such static checks.
1380-
continue;
1381-
}
1378+
case RequirementKind::Layout:
1379+
// TODO: Statically check other layout constraints, once they can
1380+
// be spelled in Swift.
1381+
if (req.getLayoutConstraint()->isClass() &&
1382+
!firstType->satisfiesClassConstraint()) {
1383+
diagnostic = diag::type_is_not_a_class;
1384+
diagnosticNote = diag::anyobject_requirement;
1385+
requirementFailure = true;
1386+
}
1387+
break;
13821388

13831389
case RequirementKind::Superclass:
13841390
// Superclass requirements.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2692,11 +2692,9 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc,
26922692
}
26932693
}
26942694

2695-
if (genericSig->requiresClass(depTy)) {
2696-
if (!contextType->isObjCExistentialType() &&
2697-
!contextType->mayHaveSuperclass())
2698-
return CheckTypeWitnessResult(tc.Context.getAnyObjectType());
2699-
}
2695+
if (genericSig->requiresClass(depTy) &&
2696+
!contextType->satisfiesClassConstraint())
2697+
return CheckTypeWitnessResult(tc.Context.getAnyObjectType());
27002698

27012699
// Success!
27022700
return CheckTypeWitnessResult();

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,7 @@ TypeChecker::getDynamicBridgedThroughObjCClass(DeclContext *dc,
223223
Type dynamicType,
224224
Type valueType) {
225225
// We can only bridge from class or Objective-C existential types.
226-
if (!dynamicType->isObjCExistentialType() &&
227-
!dynamicType->mayHaveSuperclass())
226+
if (!dynamicType->satisfiesClassConstraint())
228227
return Type();
229228

230229
// If the value type cannot be bridged, we're done.

test/Generics/class_constraint.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
struct X<T: AnyObject> { } // expected-note 4{{requirement specified as 'T' : 'AnyObject'}}
4+
5+
class C { }
6+
struct S { }
7+
8+
protocol P { }
9+
10+
let okay0: X<C>
11+
12+
struct Y<T: AnyObject> {
13+
let okay1: X<T>
14+
}
15+
16+
struct Y2<T: C> {
17+
let okay2: X<T>
18+
}
19+
20+
let bad0: X<C & P> // expected-error{{'X' requires that 'C & P' be a class type}}
21+
let bad1: X<P> // expected-error{{'X' requires that 'P' be a class type}}
22+
let bad2: X<S> // expected-error{{'X' requires that 'S' be a class type}}
23+
24+
struct Z<U> {
25+
let bad3: X<U> // expected-error{{'X' requires that 'U' be a class type}}
26+
}
27+

0 commit comments

Comments
 (0)