Skip to content

Commit 895070c

Browse files
authored
Merge pull request #14178 from DougGregor/class-constraints-4.1
[4.1] Validate class constraints for generic arguments of types
2 parents 5b1852d + d0161af commit 895070c

13 files changed

+74
-35
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/Constraints/diagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ String().asdf // expected-error {{value of type 'String' has no member 'asdf'}}
234234

235235
// <rdar://problem/21553065> Spurious diagnostic: '_' can only appear in a pattern or on the left side of an assignment
236236
protocol r21553065Protocol {}
237-
class r21553065Class<T : AnyObject> {}
238-
_ = r21553065Class<r21553065Protocol>() // expected-error {{'r21553065Protocol' is not convertible to 'AnyObject'}}
237+
class r21553065Class<T : AnyObject> {} // expected-note{{requirement specified as 'T' : 'AnyObject'}}
238+
_ = r21553065Class<r21553065Protocol>() // expected-error {{'r21553065Class' requires that 'r21553065Protocol' be a class type}}
239239

240240
// Type variables not getting erased with nested closures
241241
struct Toe {

test/Constraints/generics.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ protocol SubProto: BaseProto {}
265265
struct FullyGeneric<Foo> {} // expected-note 11 {{'Foo' declared as parameter to type 'FullyGeneric'}} expected-note 1 {{generic type 'FullyGeneric' declared here}}
266266

267267
struct AnyClassBound<Foo: AnyObject> {} // expected-note {{'Foo' declared as parameter to type 'AnyClassBound'}} expected-note {{generic type 'AnyClassBound' declared here}}
268+
// expected-note@-1{{requirement specified as 'Foo' : 'AnyObject'}}
268269
struct AnyClassBound2<Foo> where Foo: AnyObject {} // expected-note {{'Foo' declared as parameter to type 'AnyClassBound2'}}
270+
// expected-note@-1{{requirement specified as 'Foo' : 'AnyObject' [with Foo = Any]}}
269271

270272
struct ProtoBound<Foo: SubProto> {} // expected-note {{'Foo' declared as parameter to type 'ProtoBound'}} expected-note {{generic type 'ProtoBound' declared here}}
271273
struct ProtoBound2<Foo> where Foo: SubProto {} // expected-note {{'Foo' declared as parameter to type 'ProtoBound2'}}
@@ -301,11 +303,11 @@ func testFixIts() {
301303
_ = FullyGeneric<Any>()
302304

303305
_ = AnyClassBound() // expected-error {{generic parameter 'Foo' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{20-20=<AnyObject>}}
304-
_ = AnyClassBound<Any>() // expected-error {{'Any' is not convertible to 'AnyObject'}}
306+
_ = AnyClassBound<Any>() // expected-error {{'AnyClassBound' requires that 'Any' be a class type}}
305307
_ = AnyClassBound<AnyObject>()
306308

307309
_ = AnyClassBound2() // expected-error {{generic parameter 'Foo' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{21-21=<AnyObject>}}
308-
_ = AnyClassBound2<Any>() // expected-error {{'Any' is not convertible to 'AnyObject'}}
310+
_ = AnyClassBound2<Any>() // expected-error {{'AnyClassBound2' requires that 'Any' be a class type}}
309311
_ = AnyClassBound2<AnyObject>()
310312

311313
_ = ProtoBound() // expected-error {{generic parameter 'Foo' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{17-17=<<#Foo: SubProto#>>}}

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+

test/Generics/existential_restrictions.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@ class GP<T : P> {}
5353
class GOP<T : OP> {}
5454
class GCP<T : CP> {}
5555
class GSP<T : SP> {}
56-
class GAO<T : AnyObject> {}
56+
class GAO<T : AnyObject> {} // expected-note 2{{requirement specified as 'T' : 'AnyObject'}}
5757

5858
func blackHole(_ t: Any) {}
5959

6060
func testBindExistential() {
6161
blackHole(GP<P>()) // expected-error{{using 'P' as a concrete type conforming to protocol 'P' is not supported}}
6262
blackHole(GOP<OP>())
6363
blackHole(GCP<CP>()) // expected-error{{using 'CP' as a concrete type conforming to protocol 'CP' is not supported}}
64-
blackHole(GAO<P>()) // expected-error{{'P' is not convertible to 'AnyObject'}}
64+
blackHole(GAO<P>()) // expected-error{{'GAO' requires that 'P' be a class type}}
6565
blackHole(GAO<OP>())
66-
blackHole(GAO<CP>()) // expected-error{{'CP' is not convertible to 'AnyObject'}}
66+
blackHole(GAO<CP>()) // expected-error{{'GAO' requires that 'CP' be a class type}}
6767
blackHole(GSP<SP>()) // expected-error{{'SP' cannot be used as a type conforming to protocol 'SP' because 'SP' has static requirements}}
6868
blackHole(GAO<AnyObject>())
6969
}

0 commit comments

Comments
 (0)