Skip to content

Commit be28f68

Browse files
authored
Merge pull request #15094 from huonw/conditional-requirement-order
[AST] Ensure requirements are correctly identified as conditional.
2 parents bcc82d2 + 88b9f2c commit be28f68

File tree

4 files changed

+140
-33
lines changed

4 files changed

+140
-33
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,25 @@ class GenericSignatureBuilder {
816816
/// don't have associated types.
817817
Type getCanonicalTypeParameter(Type type);
818818

819+
/// For each requirement in \c sig, create a new signature without it and see
820+
/// if the requirement is satisfied in it.
821+
///
822+
/// Specifically, for each Requirement \c req in \c sig, create a new
823+
/// signature incorporating all of those in \c baseSig (if not null) and the
824+
/// other requirements from \c sig, and if \c req is satisfied in this new
825+
/// signature add it to \c redundant, otherwise add it to \c nonRedundant.
826+
///
827+
/// If \c includeRedundantRequirements is true, all requirements from \c sig
828+
/// (other than \c req) are added; if it is false, requirements already found
829+
/// to be redundant are also not added (i.e. the contents of \c redundant at
830+
/// that point in time).
831+
static void
832+
dropAndCompareEachRequirement(ASTContext &context, GenericSignature *baseSig,
833+
GenericSignature *sig,
834+
bool includeRedundantRequirements,
835+
SmallVectorImpl<Requirement> &redundant,
836+
SmallVectorImpl<Requirement> &nonRedundant);
837+
819838
/// Verify the correctness of the given generic signature.
820839
///
821840
/// This routine will test that the given generic signature is both minimal

lib/AST/GenericSignature.cpp

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@
2323
#include "swift/AST/PrettyStackTrace.h"
2424
#include "swift/AST/Types.h"
2525
#include "swift/Basic/STLExtras.h"
26+
#include "llvm/ADT/Statistic.h"
2627
#include <functional>
2728

2829
using namespace swift;
2930

31+
#define DEBUG_TYPE "Generic signature"
32+
STATISTIC(NumRedundantRequirements,
33+
"# of redundant requirements found in signature differencing");
34+
STATISTIC(NumNonRedundantRequirements,
35+
"# of non-redundant requirements found in signature differencing");
36+
3037
void ConformanceAccessPath::print(raw_ostream &out) const {
3138
interleave(begin(), end(),
3239
[&](const Entry &entry) {
@@ -770,6 +777,7 @@ bool GenericSignature::isRequirementSatisfied(Requirement requirement) {
770777

771778
SmallVector<Requirement, 4> GenericSignature::requirementsNotSatisfiedBy(
772779
GenericSignature *otherSig) {
780+
auto &ctxt = getASTContext();
773781
SmallVector<Requirement, 4> result;
774782

775783
// If the signatures are the same, all requirements are satisfied.
@@ -783,10 +791,52 @@ SmallVector<Requirement, 4> GenericSignature::requirementsNotSatisfiedBy(
783791
}
784792

785793
// Find the requirements that aren't satisfied.
786-
for (const auto &req : getRequirements()) {
787-
if (!otherSig->isRequirementSatisfied(req))
788-
result.push_back(req);
789-
}
794+
//
795+
// This is unfortunately quadratic in the size of getRequirements(), but some
796+
// arrangements of signatures result in different canonicalizations that mean
797+
// a requirement in `this` is satisfied by `otherSig`, but not in
798+
// isolation. Specifically, consider:
799+
//
800+
// otherSig == <T, U where U: P1>
801+
// this == <T, U where T: P1, T == U>`
802+
//
803+
// `T: P1` is implied by `U: P1` and `T == U` together, but just checking T:
804+
// P1 in `otherSig` won't find this, because it misses the T == U
805+
// connection. We don't currently know of a great way to handle this in
806+
// general, and instead have to do a brute-force search.
807+
808+
SmallVector<Requirement, 4> redundant;
809+
GenericSignatureBuilder::dropAndCompareEachRequirement(
810+
ctxt, otherSig, this,
811+
/*includeRedundantRequirements=*/false, redundant, result);
812+
813+
NumNonRedundantRequirements += result.size();
814+
NumRedundantRequirements += redundant.size();
815+
816+
#ifndef NDEBUG
817+
// `otherSig + result` should be `this`, so let's check it.
818+
GenericSignatureBuilder builder(ctxt);
819+
820+
// otherSig may have fewer generic parameters, so we can't use
821+
// addGenericSignature directly.
822+
823+
auto source =
824+
GenericSignatureBuilder::FloatingRequirementSource::forAbstract();
825+
for (auto gp : getGenericParams())
826+
builder.addGenericParameter(gp);
827+
for (auto req : otherSig->getRequirements())
828+
builder.addRequirement(req, source, nullptr);
829+
for (auto req : result)
830+
builder.addRequirement(req, source, nullptr);
831+
832+
auto newSig = std::move(builder).computeGenericSignature(
833+
SourceLoc(),
834+
/*allowConcreteGenericParams=*/true,
835+
/*allowBuilderToMove=*/true);
836+
837+
assert(newSig->getCanonicalSignature() == getCanonicalSignature() &&
838+
"signature differencing removed too many requirements");
839+
#endif
790840

791841
return result;
792842
}

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7438,32 +7438,58 @@ GenericSignature *GenericSignatureBuilder::computeRequirementSignature(
74387438

74397439
#pragma mark Generic signature verification
74407440

7441-
void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context,
7442-
GenericSignature *sig) {
7443-
llvm::errs() << "Validating generic signature: ";
7444-
sig->print(llvm::errs());
7445-
llvm::errs() << "\n";
7441+
void GenericSignatureBuilder::dropAndCompareEachRequirement(
7442+
ASTContext &context, GenericSignature *baseSig, GenericSignature *sig,
7443+
bool includeRedundantRequirements, SmallVectorImpl<Requirement> &redundant,
7444+
SmallVectorImpl<Requirement> &nonRedundant) {
7445+
#ifndef NDEBUG
7446+
// `baseSig`'s params should be a subset of `sig`, meaning <A where ...>, <A,
7447+
// B where ...> (respectively) is okay, but the reverse is not.
7448+
if (baseSig) {
7449+
auto canSigParams = sig->getCanonicalSignature()->getGenericParams();
7450+
for (auto baseParam :
7451+
baseSig->getCanonicalSignature()->getGenericParams()) {
7452+
assert(llvm::is_contained(canSigParams, baseParam) &&
7453+
"baseSig params aren't a subset of sig");
7454+
}
7455+
}
7456+
#endif
74467457

7447-
// Try removing each requirement in turn.
74487458
auto genericParams = sig->getGenericParams();
74497459
auto requirements = sig->getRequirements();
74507460
for (unsigned victimIndex : indices(requirements)) {
7451-
PrettyStackTraceGenericSignature debugStack("verifying", sig, victimIndex);
7461+
PrettyStackTraceGenericSignature debugStack("dropping & comparing", sig,
7462+
victimIndex);
74527463

74537464
// Form a new generic signature builder.
74547465
GenericSignatureBuilder builder(context);
74557466

7456-
// Add the generic parameters.
7467+
// Add the generic parameters, and specifically those from the main
7468+
// signature, since it may have more
74577469
for (auto gp : genericParams)
74587470
builder.addGenericParameter(gp);
74597471

7460-
// Add the requirements *except* the victim.
7472+
// Add the appropriate requirements
74617473
auto source = FloatingRequirementSource::forAbstract();
7462-
for (unsigned i : indices(requirements)) {
7463-
if (i != victimIndex)
7464-
builder.addRequirement(requirements[i], source, nullptr);
7474+
// First, the universal requirements from the base
7475+
if (baseSig) {
7476+
for (auto req : baseSig->getRequirements())
7477+
builder.addRequirement(req, source, nullptr);
74657478
}
74667479

7480+
// Next, the requirements from the interesting signature. Sometimes we have
7481+
// to add all of the leading ones, and others times only those that we've
7482+
// already decided aren't redundant:
7483+
auto leadingReqs = includeRedundantRequirements
7484+
? requirements.slice(0, victimIndex)
7485+
: nonRedundant;
7486+
// We definitely have to add all of the remaining trailing ones:
7487+
auto trailingReqs = requirements.slice(victimIndex + 1);
7488+
7489+
for (auto reqs : {leadingReqs, trailingReqs})
7490+
for (auto req : reqs)
7491+
builder.addRequirement(req, source, nullptr);
7492+
74677493
// Finalize the generic signature. If there were any errors, we formed
74687494
// an invalid signature, so just continue.
74697495
if (builder.Impl->HadAnyError) continue;
@@ -7475,23 +7501,40 @@ void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context,
74757501
/*allowConcreteGenericParams=*/true,
74767502
/*allowBuilderToMove=*/true);
74777503

7478-
// If the removed requirement is satisfied by the new generic signature,
7479-
// it is redundant. Complain.
7480-
if (newSig->isRequirementSatisfied(requirements[victimIndex])) {
7481-
SmallString<32> reqString;
7482-
{
7483-
llvm::raw_svector_ostream out(reqString);
7484-
requirements[victimIndex].print(out, PrintOptions());
7485-
}
7486-
context.Diags.diagnose(SourceLoc(), diag::generic_signature_not_minimal,
7487-
reqString, sig->getAsString());
7488-
}
7504+
auto req = requirements[victimIndex];
7505+
if (newSig->isRequirementSatisfied(req))
7506+
redundant.push_back(req);
7507+
else
7508+
nonRedundant.push_back(req);
74897509

74907510
// Canonicalize the signature to check that it is canonical.
74917511
(void)newSig->getCanonicalSignature();
74927512
}
74937513
}
74947514

7515+
void GenericSignatureBuilder::verifyGenericSignature(ASTContext &context,
7516+
GenericSignature *sig) {
7517+
llvm::errs() << "Validating generic signature: ";
7518+
sig->print(llvm::errs());
7519+
llvm::errs() << "\n";
7520+
7521+
SmallVector<Requirement, 4> redundant;
7522+
SmallVector<Requirement, 4> _nonRedundant;
7523+
dropAndCompareEachRequirement(context, /*baseSig=*/nullptr, sig,
7524+
/*includeRedundantRequirements=*/true,
7525+
redundant, _nonRedundant);
7526+
for (auto req : redundant) {
7527+
// Oops, the signature wasn't minimal: complain.
7528+
SmallString<32> reqString;
7529+
{
7530+
llvm::raw_svector_ostream out(reqString);
7531+
req.print(out, PrintOptions());
7532+
}
7533+
context.Diags.diagnose(SourceLoc(), diag::generic_signature_not_minimal,
7534+
reqString, sig->getAsString());
7535+
}
7536+
}
7537+
74957538
void GenericSignatureBuilder::verifyGenericSignaturesInModule(
74967539
ModuleDecl *module) {
74977540
LoadedFile *loadedFile = nullptr;

test/Generics/conditional_conformances.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,11 +307,7 @@ extension TwoDisjointConformances: P2 where T == String {}
307307
// expected-note@-1{{'TwoDisjointConformances<T>' declares conformance to protocol 'P2' here}}
308308

309309

310-
// FIXME: these cases should be equivalent (and both with the same output as the
311-
// first), but the second one choses T as the representative of the
312-
// equivalence class containing both T and U in the extension's generic
313-
// signature, meaning the stored conditional requirement is T: P1, which isn't
314-
// true in the original type's generic signature.
310+
// rdar://problem/34944318
315311
struct RedundancyOrderDependenceGood<T: P1, U> {}
316312
// CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundancyOrderDependenceGood<T, T>
317313
// CHECK-NEXT: (normal_conformance type=RedundancyOrderDependenceGood<T, U> protocol=P2
@@ -320,7 +316,6 @@ extension RedundancyOrderDependenceGood: P2 where U: P1, T == U {}
320316
struct RedundancyOrderDependenceBad<T, U: P1> {}
321317
// CHECK-LABEL: ExtensionDecl line={{.*}} base=RedundancyOrderDependenceBad<T, T>
322318
// CHECK-NEXT: (normal_conformance type=RedundancyOrderDependenceBad<T, U> protocol=P2
323-
// CHECK-NEXT: conforms_to: T P1
324319
// CHECK-NEXT: same_type: T U)
325320
extension RedundancyOrderDependenceBad: P2 where T: P1, T == U {}
326321

0 commit comments

Comments
 (0)