Skip to content

Commit ad89fe5

Browse files
committed
GSB: Diagnose redundant requirements on the rebuilt signature
We rebuild a generic signature after dropping conformance requirements made redundant by a superclass or concrete type requirement. When rebuilding the signature, preserve the source locations of the original requirements, and only perform diagnostics on the rebuilt signature. This fixes an issue where we would emit a redundant requirement warning even though the requirement in question was not actually redundant. This also avoids some unnecessary work. Most of the code in finalize() does not need to be run twice, once before and once after rebuilding the signature. Now we only run it after rebuilding the signature. Note that this regresses diagnostics in one narrow case where we would previously diagnose a conflicting concrete type requirement. This will be fixed once concrete type diagnostics are moved over to use the new ExplicitRequirement infrastructure, just like all other kinds already do. Fixes rdar://problem/77462797.
1 parent c746758 commit ad89fe5

File tree

7 files changed

+90
-57
lines changed

7 files changed

+90
-57
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@ class GenericSignatureBuilder {
685685
void diagnoseProtocolRefinement(
686686
const ProtocolDecl *requirementSignatureSelfProto);
687687

688-
void diagnoseRedundantRequirements() const;
688+
void diagnoseRedundantRequirements(
689+
bool onlyDiagnoseExplicitConformancesImpliedByConcrete=false) const;
689690

690691
void diagnoseConflictingConcreteTypeRequirements(
691692
const ProtocolDecl *requirementSignatureSelfProto);

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6349,8 +6349,9 @@ void
63496349
GenericSignatureBuilder::finalize(TypeArrayView<GenericTypeParamType> genericParams,
63506350
bool allowConcreteGenericParams,
63516351
const ProtocolDecl *requirementSignatureSelfProto) {
6352-
// Process any delayed requirements that we can handle now.
6353-
processDelayedRequirements();
6352+
diagnoseProtocolRefinement(requirementSignatureSelfProto);
6353+
diagnoseRedundantRequirements();
6354+
diagnoseConflictingConcreteTypeRequirements(requirementSignatureSelfProto);
63546355

63556356
{
63566357
// In various places below, we iterate over the list of equivalence classes
@@ -6383,11 +6384,6 @@ GenericSignatureBuilder::finalize(TypeArrayView<GenericTypeParamType> genericPar
63836384
}
63846385
}
63856386

6386-
computeRedundantRequirements(requirementSignatureSelfProto);
6387-
diagnoseProtocolRefinement(requirementSignatureSelfProto);
6388-
diagnoseRedundantRequirements();
6389-
diagnoseConflictingConcreteTypeRequirements(requirementSignatureSelfProto);
6390-
63916387
assert(!Impl->finalized && "Already finalized builder");
63926388
#ifndef NDEBUG
63936389
Impl->finalized = true;
@@ -7006,7 +7002,8 @@ void GenericSignatureBuilder::diagnoseProtocolRefinement(
70067002
}
70077003
}
70087004

7009-
void GenericSignatureBuilder::diagnoseRedundantRequirements() const {
7005+
void GenericSignatureBuilder::diagnoseRedundantRequirements(
7006+
bool onlyDiagnoseExplicitConformancesImpliedByConcrete) const {
70107007
for (const auto &req : Impl->ExplicitRequirements) {
70117008
auto *source = req.getSource();
70127009
auto loc = source->getLoc();
@@ -7024,6 +7021,10 @@ void GenericSignatureBuilder::diagnoseRedundantRequirements() const {
70247021
if (found == Impl->RedundantRequirements.end())
70257022
continue;
70267023

7024+
if (onlyDiagnoseExplicitConformancesImpliedByConcrete &&
7025+
Impl->ExplicitConformancesImpliedByConcrete.count(req) == 0)
7026+
continue;
7027+
70277028
// Don't diagnose explicit requirements that are implied by
70287029
// inferred requirements.
70297030
if (llvm::all_of(found->second,
@@ -8418,14 +8419,20 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir
84188419
requirementSignatureSource);
84198420
}
84208421

8421-
auto newSource = [&]() {
8422+
auto getRebuiltSource = [&](const RequirementSource *source) {
8423+
assert(!source->isDerivedRequirement());
8424+
84228425
if (auto *proto = const_cast<ProtocolDecl *>(requirementSignatureSelfProto)) {
84238426
return FloatingRequirementSource::viaProtocolRequirement(
8424-
requirementSignatureSource, proto, /*inferred=*/false);
8427+
requirementSignatureSource, proto, source->getLoc(),
8428+
source->isInferredRequirement());
84258429
}
84268430

8427-
return FloatingRequirementSource::forAbstract();
8428-
}();
8431+
if (source->isInferredRequirement())
8432+
return FloatingRequirementSource::forInferred(source->getLoc());
8433+
8434+
return FloatingRequirementSource::forExplicit(source->getLoc());
8435+
};
84298436

84308437
for (const auto &req : Impl->ExplicitRequirements) {
84318438
assert(req.getKind() != RequirementKind::SameType &&
@@ -8449,7 +8456,7 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir
84498456
!resolvedSubjectType->isTypeParameter()) {
84508457
newBuilder.addRequirement(Requirement(RequirementKind::SameType,
84518458
subjectType, resolvedSubjectType),
8452-
newSource, nullptr);
8459+
getRebuiltSource(req.getSource()), nullptr);
84538460
continue;
84548461
}
84558462

@@ -8458,7 +8465,8 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir
84588465
if (auto optReq = createRequirement(req.getKind(), resolvedSubjectType,
84598466
req.getRHS(), getGenericParams())) {
84608467
auto newReq = stripBoundDependentMemberTypes(*optReq);
8461-
newBuilder.addRequirement(newReq, newSource, nullptr);
8468+
newBuilder.addRequirement(newReq, getRebuiltSource(req.getSource()),
8469+
nullptr);
84628470
}
84638471
}
84648472

@@ -8491,7 +8499,8 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir
84918499
Requirement(RequirementKind::SameType,
84928500
subjectType, constraintType));
84938501

8494-
newBuilder.addRequirement(newReq, newSource, nullptr);
8502+
newBuilder.addRequirement(newReq, getRebuiltSource(req.getSource()),
8503+
nullptr);
84958504
}
84968505

84978506
// Wipe out the internal state of the old builder, since we don't need it anymore.
@@ -8506,22 +8515,10 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir
85068515
GenericSignature GenericSignatureBuilder::computeGenericSignature(
85078516
bool allowConcreteGenericParams,
85088517
const ProtocolDecl *requirementSignatureSelfProto) && {
8509-
// Finalize the builder, producing any necessary diagnostics.
8510-
finalize(getGenericParams(),
8511-
allowConcreteGenericParams,
8512-
requirementSignatureSelfProto);
8513-
8514-
if (Impl->RebuildingWithoutRedundantConformances) {
8515-
assert(!Impl->HadAnyError &&
8516-
"Rebuilt signature had errors");
8518+
// Process any delayed requirements that we can handle now.
8519+
processDelayedRequirements();
85178520

8518-
#ifndef NDEBUG
8519-
for (const auto &req : Impl->ExplicitConformancesImpliedByConcrete) {
8520-
assert(!isRedundantExplicitRequirement(req) &&
8521-
"Rebuilt signature still had redundant conformance requirements");
8522-
}
8523-
#endif
8524-
}
8521+
computeRedundantRequirements(requirementSignatureSelfProto);
85258522

85268523
// If any of our explicit conformance requirements were implied by
85278524
// superclass or concrete same-type requirements, we have to build the
@@ -8535,12 +8532,30 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
85358532
if (!Impl->RebuildingWithoutRedundantConformances &&
85368533
!Impl->HadAnyError &&
85378534
!Impl->ExplicitConformancesImpliedByConcrete.empty()) {
8535+
diagnoseRedundantRequirements(
8536+
/*onlyDiagnoseExplicitConformancesImpliedByConcrete=*/true);
8537+
85388538
return std::move(*this).rebuildSignatureWithoutRedundantRequirements(
85398539
allowConcreteGenericParams,
85408540
requirementSignatureSelfProto);
85418541
}
85428542

8543-
// Collect the requirements placed on the generic parameter types.
8543+
if (Impl->RebuildingWithoutRedundantConformances) {
8544+
#ifndef NDEBUG
8545+
for (const auto &req : Impl->ExplicitConformancesImpliedByConcrete) {
8546+
assert(!isRedundantExplicitRequirement(req) &&
8547+
"Rebuilt signature still had redundant conformance requirements");
8548+
}
8549+
#endif
8550+
}
8551+
8552+
// Diagnose redundant requirements, check for recursive concrete types
8553+
// and compute minimized same-type requirements.
8554+
finalize(getGenericParams(),
8555+
allowConcreteGenericParams,
8556+
requirementSignatureSelfProto);
8557+
8558+
// Collect all non-redundant explicit requirements.
85448559
SmallVector<Requirement, 4> requirements;
85458560
enumerateRequirements(getGenericParams(), requirements);
85468561

test/Generics/derived_via_concrete.swift

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,48 +26,32 @@ func derivedViaConcreteX1<A, B>(_: A, _: B)
2626
// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}}
2727
// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}}
2828

29-
// FIXME: We should not diagnose 'B : P' as redundant, even though it
30-
// really is, because it was made redundant by an inferred requirement.
31-
3229
// CHECK: Generic signature: <A, B where A : X<B>, B : P>
3330
func derivedViaConcreteX2<A, B>(_: A, _: B)
3431
where A : U, B : P, A : X<B> {}
3532
// expected-warning@-1 {{redundant conformance constraint 'A' : 'U'}}
3633
// expected-note@-2 {{conformance constraint 'A' : 'U' implied here}}
37-
// expected-warning@-3 {{redundant conformance constraint 'B' : 'P'}}
38-
// expected-note@-4 {{conformance constraint 'B' : 'P' implied here}}
3934

4035
// CHECK: Generic signature: <A, B where A : Y<B>, B : C>
4136
func derivedViaConcreteY1<A, B>(_: A, _: B)
4237
where A : V, A : Y<B> {}
4338
// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}}
4439
// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}}
4540

46-
// FIXME: We should not diagnose 'B : C' as redundant, even though it
47-
// really is, because it was made redundant by an inferred requirement.
48-
4941
// CHECK: Generic signature: <A, B where A : Y<B>, B : C>
5042
func derivedViaConcreteY2<A, B>(_: A, _: B)
5143
where A : V, B : C, A : Y<B> {}
5244
// expected-warning@-1 {{redundant conformance constraint 'A' : 'V'}}
5345
// expected-note@-2 {{conformance constraint 'A' : 'V' implied here}}
54-
// expected-warning@-3 {{redundant superclass constraint 'B' : 'C'}}
55-
// expected-note@-4 {{superclass constraint 'B' : 'C' implied here}}
5646

5747
// CHECK: Generic signature: <A, B where A : Z<B>, B : AnyObject>
5848
func derivedViaConcreteZ1<A, B>(_: A, _: B)
5949
where A : W, A : Z<B> {}
6050
// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}}
6151
// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}}
6252

63-
// FIXME: We should not diagnose 'B : AnyObject' as redundant, even
64-
// though it really is, because it was made redundant by an inferred
65-
// requirement.
66-
6753
// CHECK: Generic signature: <A, B where A : Z<B>, B : AnyObject>
6854
func derivedViaConcreteZ2<A, B>(_: A, _: B)
6955
where A : W, B : AnyObject, A : Z<B> {}
7056
// expected-warning@-1 {{redundant conformance constraint 'A' : 'W'}}
7157
// expected-note@-2 {{conformance constraint 'A' : 'W' implied here}}
72-
// expected-warning@-3 {{redundant constraint 'B' : 'AnyObject'}}
73-
// expected-note@-4 {{constraint 'B' : 'AnyObject' implied here}}

test/Generics/rdar77462797.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
4+
protocol P {}
5+
6+
protocol Q {
7+
associatedtype T : P
8+
}
9+
10+
class S<T : P> : Q {}
11+
12+
struct G<X, Y> where X : Q {
13+
// no redundancies
14+
func foo1() where X == S<Y> {}
15+
// CHECK: Generic signature: <X, Y where X == S<Y>, Y : P>
16+
17+
// 'Y : P' is redundant, but only via an inferred requirement from S<Y>,
18+
// so we should not diagnose it
19+
func foo2() where X == S<Y>, Y : P {}
20+
// CHECK: Generic signature: <X, Y where X == S<Y>, Y : P>
21+
22+
// 'X : Q' is redundant
23+
func foo3() where X : Q, X == S<Y>, Y : P {}
24+
// expected-warning@-1 {{redundant conformance constraint 'X' : 'Q'}}
25+
// expected-note@-2 {{conformance constraint 'X' : 'Q' implied here}}
26+
// CHECK: Generic signature: <X, Y where X == S<Y>, Y : P>
27+
28+
// 'T.T : P' is redundant
29+
func foo4<T : Q>(_: T) where X == S<Y>, T.T : P {}
30+
// expected-warning@-1 {{redundant conformance constraint 'T.T' : 'P'}}
31+
// expected-note@-2 {{conformance constraint 'T.T' : 'P' implied here}}
32+
// CHECK: Generic signature: <X, Y, T where X == S<Y>, Y : P, T : Q>
33+
}
34+
35+
func foo<X, Y>(_: X, _: Y) where X : Q, X : S<Y>, Y : P {}
36+
// expected-warning@-1 {{redundant conformance constraint 'X' : 'Q'}}
37+
// expected-note@-2 {{conformance constraint 'X' : 'Q' implied here}}
38+
// CHECK: Generic signature: <X, Y where X : S<Y>, Y : P>

test/Generics/requirement_inference.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,7 @@ struct X24<T: P20> : P24 {
328328
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A == X24<τ_0_0.B>, τ_0_0.B : P20>
329329
protocol P25a {
330330
associatedtype A: P24 // expected-warning{{redundant conformance constraint 'Self.A' : 'P24'}}
331-
// expected-note@-1 {{conformance constraint 'Self.B' : 'P20' implied here}}
332331
associatedtype B: P20 where A == X24<B> // expected-note{{conformance constraint 'Self.A' : 'P24' implied here}}
333-
// expected-warning@-1 {{redundant conformance constraint 'Self.B' : 'P20'}}
334332
}
335333

336334
// CHECK-LABEL: .P25b@
@@ -365,13 +363,8 @@ struct X26<T: X3> : P26 {
365363
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.A == X26<τ_0_0.B>, τ_0_0.B : X3>
366364
protocol P27a {
367365
associatedtype A: P26 // expected-warning{{redundant conformance constraint 'Self.A' : 'P26'}}
368-
// expected-note@-1 {{superclass constraint 'Self.B' : 'X3' implied here}}
369366

370367
associatedtype B: X3 where A == X26<B> // expected-note{{conformance constraint 'Self.A' : 'P26' implied here}}
371-
// expected-warning@-1 {{redundant superclass constraint 'Self.B' : 'X3'}}
372-
373-
// FIXME: The above warning should not be emitted -- while the requirement
374-
// really is redundant, it is made redundant by an inferred requirement.
375368
}
376369

377370
// CHECK-LABEL: .P27b@

test/Generics/sr13884.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %target-typecheck-verify-swift
2-
// RUN: %target-swift-frontend -emit-ir -debug-generic-signatures %s 2>&1 | %FileCheck %s
2+
// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
33

44
public protocol P {
55
associatedtype A : Q where A.B == Self
@@ -27,8 +27,10 @@ public class D : Q {
2727
public func takesBoth1<T : P & C>(_: T) {}
2828
// expected-warning@-1 {{redundant conformance constraint 'T' : 'P'}}
2929
// expected-note@-2 {{conformance constraint 'T' : 'P' implied here}}
30+
// expected-error@-3 {{same-type requirement makes generic parameter 'T' non-generic}}
3031

3132
// CHECK-LABEL: Generic signature: <U where U == D.B>
3233
public func takesBoth2<U : C & P>(_: U) {}
3334
// expected-warning@-1 {{redundant conformance constraint 'U' : 'P'}}
3435
// expected-note@-2 {{conformance constraint 'U' : 'P' implied here}}
36+
// expected-error@-3 {{same-type requirement makes generic parameter 'U' non-generic}}

test/attr/attr_specialize.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ struct FloatElement : HasElt {
9595
typealias Element = Float
9696
}
9797
@_specialize(where T == FloatElement)
98-
@_specialize(where T == IntElement) // expected-error{{'T.Element' cannot be equal to both 'IntElement.Element' (aka 'Int') and 'Float'}}
98+
@_specialize(where T == IntElement) // FIXME e/xpected-error{{'T.Element' cannot be equal to both 'IntElement.Element' (aka 'Int') and 'Float'}}
9999
func sameTypeRequirement<T : HasElt>(_ t: T) where T.Element == Float {}
100100

101101
@_specialize(where T == Sub)

0 commit comments

Comments
 (0)