Skip to content

Commit 9339443

Browse files
committed
RequirementMachine: Diagnose recursive requirements
Note the test cases in abstract_type_witnesses used to pass but are now rejected. This is fine, because doing anything more complicated used to crash, and the GSB would crash or misbehave with these examples.
1 parent 42407bb commit 9339443

9 files changed

+145
-19
lines changed

include/swift/AST/DiagnosticsSema.def

+4
Original file line numberDiff line numberDiff line change
@@ -2508,6 +2508,10 @@ ERROR(recursive_generic_signature,none,
25082508
"%0 %1 has self-referential generic requirements", (DescriptiveDeclKind, DeclBaseName))
25092509
ERROR(recursive_generic_signature_extension,none,
25102510
"extension of %0 %1 has self-referential generic requirements", (DescriptiveDeclKind, DeclBaseName))
2511+
ERROR(recursive_same_type_constraint,none,
2512+
"same-type constraint %0 == %1 is recursive", (Type, Type))
2513+
ERROR(recursive_superclass_constraint,none,
2514+
"superclass constraint %0 : %1 is recursive", (Type, Type))
25112515
ERROR(requires_same_concrete_type,none,
25122516
"generic signature requires types %0 and %1 to be the same", (Type, Type))
25132517
WARNING(redundant_conformance_constraint,none,

lib/AST/RequirementMachine/Diagnostics.cpp

+42-3
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ bool swift::rewriting::diagnoseRequirementErrors(
137137
break;
138138
}
139139

140+
case RequirementError::Kind::RecursiveRequirement: {
141+
auto requirement = error.requirement;
142+
143+
if (requirement.hasError())
144+
break;
145+
146+
assert(requirement.getKind() == RequirementKind::SameType ||
147+
requirement.getKind() == RequirementKind::Superclass);
148+
149+
ctx.Diags.diagnose(loc,
150+
(requirement.getKind() == RequirementKind::SameType ?
151+
diag::recursive_same_type_constraint :
152+
diag::recursive_superclass_constraint),
153+
requirement.getFirstType(),
154+
requirement.getSecondType());
155+
156+
diagnosedError = true;
157+
break;
158+
}
159+
140160
case RequirementError::Kind::RedundantRequirement: {
141161
// We only emit redundant requirement warnings if the user passed
142162
// the -warn-redundant-requirements frontend flag.
@@ -390,7 +410,7 @@ getRequirementForDiagnostics(Type subject, Symbol property,
390410
}
391411
}
392412

393-
void RewriteSystem::computeConflictDiagnostics(
413+
void RewriteSystem::computeConflictingRequirementDiagnostics(
394414
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc,
395415
const PropertyMap &propertyMap,
396416
TypeArrayView<GenericTypeParamType> genericParams) {
@@ -427,11 +447,30 @@ void RewriteSystem::computeConflictDiagnostics(
427447
}
428448
}
429449

450+
void RewriteSystem::computeRecursiveRequirementDiagnostics(
451+
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc,
452+
const PropertyMap &propertyMap,
453+
TypeArrayView<GenericTypeParamType> genericParams) {
454+
for (unsigned ruleID : RecursiveRules) {
455+
const auto &rule = getRule(ruleID);
456+
457+
assert(isInMinimizationDomain(rule.getRHS()[0].getRootProtocol()));
458+
459+
Type subjectType = propertyMap.getTypeForTerm(rule.getRHS(), genericParams);
460+
errors.push_back(RequirementError::forRecursiveRequirement(
461+
getRequirementForDiagnostics(subjectType, *rule.isPropertyRule(),
462+
propertyMap, genericParams, MutableTerm()),
463+
signatureLoc));
464+
}
465+
}
466+
430467
void RequirementMachine::computeRequirementDiagnostics(
431468
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc) {
432469
System.computeRedundantRequirementDiagnostics(errors);
433-
System.computeConflictDiagnostics(errors, signatureLoc, Map,
434-
getGenericParams());
470+
System.computeConflictingRequirementDiagnostics(errors, signatureLoc, Map,
471+
getGenericParams());
472+
System.computeRecursiveRequirementDiagnostics(errors, signatureLoc, Map,
473+
getGenericParams());
435474
}
436475

437476
std::string RequirementMachine::getRuleAsStringForDiagnostics(

lib/AST/RequirementMachine/Diagnostics.h

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ struct RequirementError {
3636
InvalidRequirementSubject,
3737
/// A pair of conflicting requirements, T == Int, T == String
3838
ConflictingRequirement,
39+
/// A recursive requirement, e.g. T == G<T.A>.
40+
RecursiveRequirement,
3941
/// A redundant requirement, e.g. T == T.
4042
RedundantRequirement,
4143
} kind;
@@ -86,6 +88,11 @@ struct RequirementError {
8688
SourceLoc loc) {
8789
return {Kind::RedundantRequirement, req, loc};
8890
}
91+
92+
static RequirementError forRecursiveRequirement(Requirement req,
93+
SourceLoc loc) {
94+
return {Kind::RecursiveRequirement, req, loc};
95+
}
8996
};
9097

9198
/// Policy for the fixit that transforms 'T : S' where 'S' is not a protocol

lib/AST/RequirementMachine/RewriteSystem.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,15 @@ class RewriteSystem final {
229229

230230
void computeRedundantRequirementDiagnostics(SmallVectorImpl<RequirementError> &errors);
231231

232-
void computeConflictDiagnostics(SmallVectorImpl<RequirementError> &errors,
233-
SourceLoc signatureLoc,
234-
const PropertyMap &map,
235-
TypeArrayView<GenericTypeParamType> genericParams);
232+
void computeConflictingRequirementDiagnostics(SmallVectorImpl<RequirementError> &errors,
233+
SourceLoc signatureLoc,
234+
const PropertyMap &map,
235+
TypeArrayView<GenericTypeParamType> genericParams);
236+
237+
void computeRecursiveRequirementDiagnostics(SmallVectorImpl<RequirementError> &errors,
238+
SourceLoc signatureLoc,
239+
const PropertyMap &map,
240+
TypeArrayView<GenericTypeParamType> genericParams);
236241

237242
private:
238243
struct CriticalPair {

test/Constraints/same_types.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ func testSameTypeCommutativity5<U, T: P1>(_ t: T, _ u: U)
367367
where PPP & P3 == T.Assoc { } // Ok, equivalent to T.Assoc == PPP & P3
368368

369369
// CHECK-LABEL: same_types.(file).testSameTypeCommutativity6@
370-
// CHECK-NEXT: Generic signature: <U, T where T : P1>
370+
// CHECK-NEXT: Generic signature: <U, T where T : P1, T.[P1]Assoc == <<error type>>>
371371
func testSameTypeCommutativity6<U, T: P1>(_ t: T, _ u: U)
372372
where U & P3 == T.Assoc { } // Equivalent to T.Assoc == U & P3
373373
// expected-error@-1 {{non-protocol, non-class type 'U' cannot be used within a protocol-constrained type}}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
2-
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -disable-requirement-machine-concrete-contraction 2>&1 | %FileCheck %s
1+
// RUN: %target-swift-frontend -typecheck -verify %s -debug-generic-signatures 2>&1 | %FileCheck %s
2+
// RUN: %target-swift-frontend -typecheck -verify %s -debug-generic-signatures -disable-requirement-machine-concrete-contraction 2>&1 | %FileCheck %s
33

44
protocol P {
55
associatedtype T
@@ -15,49 +15,60 @@ protocol Q {
1515
protocol R {}
1616

1717
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q1@
18-
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<Self.[Q]A.[P]T>, Self.[Q1]B : P, Self.[Q]A.[P]T == Self.[Q1]B.[P]T>
18+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<<<error type>>>, Self.[Q1]B : P, Self.[Q]A.[P]T == Self.[Q1]B.[P]T>
1919

2020
// GSB: Non-canonical requirement
21+
// expected-error@+1 {{same-type constraint 'Self.A' == 'G<Self.A.T>' is recursive}}
2122
protocol Q1 : Q {
2223
associatedtype B : P where A == G<B.T>
2324
}
2425

26+
// This used to crash
27+
func useQ1<T : Q1>(_: T) -> T.A.T.Type {
28+
return T.A.T.Type
29+
}
30+
2531
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q1a@
26-
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<Self.[Q]A.[P]T>, Self.[Q1a]B : P, Self.[Q]A.[P]T : R, Self.[Q]A.[P]T == Self.[Q1a]B.[P]T>
32+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<<<error type>>>, Self.[Q1a]B : P, Self.[Q]A.[P]T : R, Self.[Q]A.[P]T == Self.[Q1a]B.[P]T>
2733

2834
// GSB: Missing requirement
35+
// expected-error@+1 {{same-type constraint 'Self.A' == 'G<Self.A.T>' is recursive}}
2936
protocol Q1a : Q {
3037
associatedtype B : P where A.T : R, A == G<B.T>
3138
}
3239

3340
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q1b@
34-
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<Self.[Q]A.[P]T>, Self.[Q1b]B : P, Self.[Q]A.[P]T : R, Self.[Q]A.[P]T == Self.[Q1b]B.[P]T>
41+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<<<error type>>>, Self.[Q1b]B : P, Self.[Q]A.[P]T : R, Self.[Q]A.[P]T == Self.[Q1b]B.[P]T>
3542

3643
// GSB: Non-canonical requirement
44+
// expected-error@+1 {{same-type constraint 'Self.A' == 'G<Self.A.T>' is recursive}}
3745
protocol Q1b : Q {
3846
associatedtype B : P where B.T : R, A == G<B.T>
3947
}
4048

4149
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q2@
42-
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<Self.[Q]A.[P]T>, Self.[Q2]B : P, Self.[Q]A.[P]T == Self.[Q2]B.[P]T>
50+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<<<error type>>>, Self.[Q2]B : P, Self.[Q]A.[P]T == Self.[Q2]B.[P]T>
4351

4452
// GSB: Missing requirement
53+
// expected-error@+1 {{same-type constraint 'Self.A' == 'G<Self.A.T>' is recursive}}
4554
protocol Q2 : Q {
4655
associatedtype B : P where A.T == B.T, A == G<B.T>
4756
}
4857

4958
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q3@
50-
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<Self.[Q]A.[P]T>, Self.[Q3]B : P, Self.[Q]A.[P]T == Self.[Q3]B.[P]T>
59+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<<<error type>>>, Self.[Q3]B : P, Self.[Q]A.[P]T == Self.[Q3]B.[P]T>
5160

5261
// GSB: Unsupported recursive requirement
62+
// expected-error@+1 {{same-type constraint 'Self.A' == 'G<Self.A.T>' is recursive}}
5363
protocol Q3 : Q {
5464
associatedtype B : P where A == G<A.T>, A.T == B.T
5565
}
5666

5767
// CHECK-LABEL: abstract_type_witnesses_in_protocols.(file).Q4@
58-
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<Self.[Q]A.[P]T>, Self.[Q4]B : P, Self.[Q]A.[P]T == Self.[Q4]B.[P]T>
68+
// CHECK-NEXT: Requirement signature: <Self where Self : Q, Self.[Q]A == G<<<error type>>>, Self.[Q4]B : P, Self.[Q]A.[P]T == Self.[Q4]B.[P]T>
5969

6070
// GSB: Unsupported recursive requirement
71+
// expected-error@+1 {{same-type constraint 'Self.A' == 'G<Self.A.T>' is recursive}}
6172
protocol Q4 : Q {
6273
associatedtype B : P where A.T == B.T, A == G<A.T>
6374
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol P {
4+
associatedtype A
5+
}
6+
7+
struct S<A>: P {}
8+
9+
// expected-error@+1 {{same-type constraint 'Self' == 'S<Self.A>' is recursive}}
10+
extension P where Self == S<A> {
11+
func f1(_: A) -> Self {}
12+
}
13+
14+
// expected-error@+1 {{same-type constraint 'Self' == 'S<Self.A>' is recursive}}
15+
extension P where Self == S<Self.A> {
16+
func f2(_: A) -> Self {}
17+
}
18+
19+
class C<A>: P {}
20+
21+
// expected-note@+3 {{while resolving type 'A'}}
22+
// expected-note@+2 {{while resolving type 'C<A>'}}
23+
// expected-error@+1 {{extension of protocol 'P' has self-referential generic requirements}}
24+
extension P where Self : C<A> {
25+
func f(_: A) -> Self {}
26+
}
27+
28+
// expected-error@+1 {{superclass constraint 'Self' : 'C<Self.A>' is recursive}}
29+
extension P where Self : C<Self.A> {
30+
func f(_: A) -> Self {}
31+
}
32+
33+
// https://github.com/apple/swift/issues/59476
34+
protocol Fruit {
35+
associatedtype Output
36+
var output: Output? { get }
37+
}
38+
39+
struct MapFruit<F: Fruit, Output>: Fruit {
40+
var output: Output? { fatalError() }
41+
}
42+
43+
struct Garden<F: Fruit>: Fruit {
44+
// expected-error@+1 {{same-type constraint 'F' == 'MapFruit<G, F.Output>' is recursive}}
45+
init<G: Fruit>(fruit1: G) where F == MapFruit<G, Output> { }
46+
47+
// expected-error@+1 {{same-type constraint 'F' == 'MapFruit<G, F.Output>' is recursive}}
48+
init<G: Fruit>(fruit2: G) where F == MapFruit<G, F.Output> { }
49+
50+
var output: F.Output? { fatalError() }
51+
}
52+
53+
// rdar://problem/90062518
54+
extension Slice {
55+
// expected-error@+1 {{same-type constraint 'Base' == 'UnsafeBufferPointer<Base.Element>' is recursive}}
56+
public func withMemoryRebound<T, Result>(
57+
to type: T.Type, _ body: (UnsafeBufferPointer<T>) throws -> Result
58+
) rethrows -> Result where Base == UnsafeBufferPointer<Element> {
59+
let rebased = UnsafeBufferPointer<Element>(rebased: self)
60+
return try rebased.withMemoryRebound(to: T.self, body)
61+
}
62+
}

test/attr/attr_specialize.swift

-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ class NonSub {}
2828
@_specialize(where T == S<Int>)
2929
@_specialize(where T == Int, U == Int) // expected-error{{cannot find type 'U' in scope}},
3030
@_specialize(where T == T1) // expected-error{{cannot find type 'T1' in scope}}
31-
// expected-error@-1 {{too few generic parameters are specified in '_specialize' attribute (got 0, but expected 1)}}
32-
// expected-note@-2 {{missing constraint for 'T' in '_specialize' attribute}}
3331
public func oneGenericParam<T>(_ t: T) -> T {
3432
return t
3533
}

test/type/opaque.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func blibble(blobble: some P) {}
8787
func blib() -> P & some Q { return 1 } // expected-error{{'some' should appear at the beginning}}
8888
func blab() -> some P? { return 1 } // expected-error{{must specify only}} expected-note{{did you mean to write an optional of an 'opaque' type?}}
8989
func blorb<T: some P>(_: T) { } // expected-error{{'some' types are only permitted}}
90-
func blub<T>() -> T where T == some P { return 1 } // expected-error{{'some' types are only permitted}} expected-error{{cannot convert}}
90+
func blub<T>() -> T where T == some P { return 1 } // expected-error{{'some' types are only permitted}}
9191

9292
protocol OP: some P {} // expected-error{{'some' types are only permitted}}
9393

0 commit comments

Comments
 (0)