Skip to content

Commit 211efbc

Browse files
committed
RequirementMachine: Diagnose recursive requirements
1 parent 1e4906c commit 211efbc

File tree

5 files changed

+124
-7
lines changed

5 files changed

+124
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
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

Lines changed: 42 additions & 3 deletions
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 9 additions & 4 deletions
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 {
Lines changed: 62 additions & 0 deletions
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+
}

0 commit comments

Comments
 (0)