Skip to content

Commit ee99c8a

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.
1 parent 3806d56 commit ee99c8a

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
@@ -1408,6 +1408,10 @@ NOTE(candidate_types_inheritance_requirement,none,
14081408
(Type, Type, Type, Type, StringRef))
14091409
NOTE(types_not_equal_requirement,none,
14101410
"requirement specified as %0 == %1%2", (Type, Type, StringRef))
1411+
ERROR(type_is_not_a_class,none,
1412+
"%0 requires that %1 be a class type", (Type, Type, Type))
1413+
NOTE(anyobject_requirement,none,
1414+
"requirement specified as %0 : 'AnyObject'%2", (Type, Type, StringRef))
14111415
ERROR(non_class_cannot_conform_to_class_protocol,none,
14121416
"non-class type %0 cannot conform to class protocol %1",
14131417
(Type, Type))

include/swift/AST/Types.h

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

699+
/// Determine whether this type satisfies a class layout constraint, written
700+
/// `T: AnyObject` in the source.
701+
///
702+
/// A class layout constraint is satisfied when we have a single retainable
703+
/// pointer as the representation, which includes:
704+
/// - @objc existentials
705+
/// - class constrained archetypes
706+
/// - classes
707+
bool satisfiesClassConstraint();
708+
699709
/// \brief Determine whether this type can be used as a base type for AST
700710
/// name lookup, which is the case for nominal types, protocol compositions
701711
/// and archetypes.

lib/AST/Type.cpp

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

1387+
bool TypeBase::satisfiesClassConstraint() {
1388+
return mayHaveSuperclass() || isObjCExistentialType();
1389+
}
1390+
13871391
Type TypeBase::getSuperclass() {
13881392
auto *nominalDecl = getAnyNominal();
13891393
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
@@ -2282,10 +2282,8 @@ TypeConverter::checkForABIDifferences(SILType type1, SILType type2) {
22822282
// Classes, class-constrained archetypes, and pure-ObjC existential types
22832283
// all have single retainable pointer representation; optionality change
22842284
// is allowed.
2285-
if ((type1.getSwiftRValueType()->mayHaveSuperclass() ||
2286-
type1.getSwiftRValueType()->isObjCExistentialType()) &&
2287-
(type2.getSwiftRValueType()->mayHaveSuperclass() ||
2288-
type2.getSwiftRValueType()->isObjCExistentialType()))
2285+
if (type1.getSwiftRValueType()->satisfiesClassConstraint() &&
2286+
type2.getSwiftRValueType()->satisfiesClassConstraint())
22892287
return ABIDifference::Trivial;
22902288

22912289
// 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
@@ -1297,14 +1297,7 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
12971297
if (auto layoutConstraint = layout.getLayoutConstraint()) {
12981298
if (layoutConstraint->isClass()) {
12991299
if (kind == ConstraintKind::ConformsTo) {
1300-
// Conformance to AnyObject is defined by having a single
1301-
// retainable pointer representation:
1302-
//
1303-
// - @objc existentials
1304-
// - class constrained archetypes
1305-
// - classes
1306-
if (!type1->isObjCExistentialType() &&
1307-
!type1->mayHaveSuperclass())
1300+
if (!type1->satisfiesClassConstraint())
13081301
return getTypeMatchFailure(locator);
13091302
} else {
13101303
// 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
@@ -2821,9 +2821,7 @@ bool TypeChecker::checkedCastMaySucceed(Type t1, Type t2, DeclContext *dc) {
28212821

28222822
bool TypeChecker::isSubstitutableFor(Type type, ArchetypeType *archetype,
28232823
DeclContext *dc) {
2824-
if (archetype->requiresClass() &&
2825-
!type->mayHaveSuperclass() &&
2826-
!type->isObjCExistentialType())
2824+
if (archetype->requiresClass() && !type->satisfiesClassConstraint())
28272825
return false;
28282826

28292827
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(
@@ -1380,12 +1382,16 @@ RequirementCheckResult TypeChecker::checkGenericArguments(
13801382
break;
13811383
}
13821384

1383-
case RequirementKind::Layout: {
1384-
// TODO: Statically check if a the first type
1385-
// conforms to the layout constraint, once we
1386-
// support such static checks.
1387-
continue;
1388-
}
1385+
case RequirementKind::Layout:
1386+
// TODO: Statically check other layout constraints, once they can
1387+
// be spelled in Swift.
1388+
if (req.getLayoutConstraint()->isClass() &&
1389+
!firstType->satisfiesClassConstraint()) {
1390+
diagnostic = diag::type_is_not_a_class;
1391+
diagnosticNote = diag::anyobject_requirement;
1392+
requirementFailure = true;
1393+
}
1394+
break;
13891395

13901396
case RequirementKind::Superclass:
13911397
// Superclass requirements.

lib/Sema/TypeCheckProtocol.cpp

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

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

27002698
// Success!
27012699
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)