ASTPrinter: Refactor printing of RequirementSignatures

This commit is contained in:
Slava Pestov
2024-01-31 12:37:45 -05:00
parent 2355cb4090
commit 23b1690f6b
10 changed files with 164 additions and 79 deletions

View File

@@ -73,6 +73,16 @@ public:
GenericSignatureErrors getErrors() const {
return Errors;
}
void getRequirementsWithInverses(
ProtocolDecl *owner,
SmallVector<Requirement, 2> &reqs,
SmallVector<InverseRequirement, 2> &inverses) const;
void print(ProtocolDecl *owner, raw_ostream &OS,
const PrintOptions &Options = PrintOptions()) const;
void print(ProtocolDecl *owner, ASTPrinter &Printer,
const PrintOptions &Opts = PrintOptions()) const;
};
} // end namespace swift

View File

@@ -970,6 +970,10 @@ public:
ArrayRef<InverseRequirement> inverses,
bool &isFirstReq, unsigned flags,
llvm::function_ref<bool(const Requirement &)> filter);
void printRequirementSignature(ProtocolDecl *owner,
RequirementSignature sig,
unsigned flags,
TypeDecl *attachingTo);
void printRequirement(const Requirement &req);
void printRequirement(const InverseRequirement &inverse,
bool forInherited);
@@ -1566,46 +1570,20 @@ bestRequirementPrintLocation(ProtocolDecl *proto, const Requirement &req) {
}
void PrintAST::printInheritedFromRequirementSignature(ProtocolDecl *proto,
TypeDecl* attachingTo) {
Type attachedGP[1];
if (auto proto = dyn_cast<ProtocolDecl>(attachingTo))
attachedGP[0] = proto->getSelfInterfaceType();
else if (auto assoc = dyn_cast<AssociatedTypeDecl>(attachingTo))
attachedGP[0] = assoc->getDeclaredInterfaceType();
else
llvm_unreachable("nonexhaustive");
printGenericSignature(
proto->getRequirementSignatureAsGenericSignature(),
PrintInherited,
[&](const Requirement &req) {
// Skip the inferred 'Self : AnyObject' constraint if this is an
// @objc protocol.
if ((req.getKind() == RequirementKind::Layout) &&
req.getFirstType()->isEqual(proto->getSelfInterfaceType()) &&
req.getLayoutConstraint()->getKind() ==
LayoutConstraintKind::Class &&
proto->isObjC()) {
return false;
}
auto location = bestRequirementPrintLocation(proto, req);
return location.AttachedTo == attachingTo && !location.InWhereClause;
});
TypeDecl *attachingTo) {
printRequirementSignature(
proto, proto->getRequirementSignature(),
PrintInherited | PrintInverseRequirements,
attachingTo);
}
void PrintAST::printWhereClauseFromRequirementSignature(ProtocolDecl *proto,
TypeDecl *attachingTo) {
unsigned flags = PrintRequirements;
unsigned flags = PrintRequirements | PrintInverseRequirements;
if (isa<AssociatedTypeDecl>(attachingTo))
flags |= SwapSelfAndDependentMemberType;
printGenericSignature(
proto->getRequirementSignatureAsGenericSignature(),
flags,
[&](const Requirement &req) {
auto location = bestRequirementPrintLocation(proto, req);
return location.AttachedTo == attachingTo && location.InWhereClause;
});
printRequirementSignature(proto, proto->getRequirementSignature(), flags,
attachingTo);
}
/// A helper function to return the depth of a requirement.
@@ -1926,6 +1904,62 @@ void PrintAST::printSingleDepthOfGenericSignature(
Printer << ">";
}
void PrintAST::printRequirementSignature(ProtocolDecl *owner,
RequirementSignature sig,
unsigned flags,
TypeDecl *attachingTo) {
SmallVector<Requirement, 2> requirements;
SmallVector<InverseRequirement, 2> inverses;
if (flags & PrintInverseRequirements) {
sig.getRequirementsWithInverses(owner, requirements, inverses);
} else {
requirements.append(sig.getRequirements().begin(),
sig.getRequirements().end());
}
if (attachingTo) {
llvm::erase_if(requirements,
[&](Requirement req) {
// Skip the inferred 'Self : AnyObject' constraint if this is an
// @objc protocol.
if ((req.getKind() == RequirementKind::Layout) &&
req.getFirstType()->isEqual(owner->getSelfInterfaceType()) &&
req.getLayoutConstraint()->getKind() ==
LayoutConstraintKind::Class &&
owner->isObjC()) {
return true;
}
auto location = bestRequirementPrintLocation(owner, req);
if (location.AttachedTo != attachingTo)
return true;
if (flags & PrintRequirements)
return !location.InWhereClause;
return location.InWhereClause;
});
auto interfaceTy =
(isa<ProtocolDecl>(attachingTo)
? cast<ProtocolDecl>(attachingTo)->getSelfInterfaceType()
: cast<AssociatedTypeDecl>(attachingTo)->getDeclaredInterfaceType());
llvm::erase_if(inverses,
[&](InverseRequirement req) {
// We print inverse requirements in the inheritance clause only.
if (flags & PrintRequirements)
return true;
return !req.subject->isEqual(interfaceTy);
});
}
printSingleDepthOfGenericSignature(
owner->getGenericSignature().getGenericParams(), requirements, inverses,
flags, [&](Requirement) { return true; });
}
void PrintAST::printRequirement(const Requirement &req) {
SmallVector<Type, 2> rootParameterPacks;
getTransformedType(req.getFirstType())
@@ -2786,13 +2820,12 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType,
void PrintAST::printSynthesizedExtensionImpl(Type ExtendedType,
ExtensionDecl *ExtDecl) {
auto printRequirementsFrom = [&](ExtensionDecl *ED, bool &IsFirst) {
// NOTE: As with normal extensions, we do not collapse defaults.
auto Sig = ED->getGenericSignature();
printSingleDepthOfGenericSignature(Sig.getGenericParams(),
Sig.getRequirements(),
/*inverses=*/{},
IsFirst,
PrintRequirements,
PrintRequirements | PrintInverseRequirements,
[](const Requirement &Req){
return true;
});
@@ -7946,6 +7979,22 @@ void GenericSignature::print(ASTPrinter &Printer,
PrintAST(Printer, Opts).printGenericSignature(*this, flags);
}
void RequirementSignature::print(ProtocolDecl *owner,
raw_ostream &OS,
const PrintOptions &Opts) const {
StreamPrinter Printer(OS);
print(owner, Printer, Opts);
}
void RequirementSignature::print(ProtocolDecl *owner,
ASTPrinter &Printer,
const PrintOptions &Opts) const {
auto flags = PrintAST::PrintParams | PrintAST::PrintRequirements;
if (Opts.PrintInverseRequirements)
flags |= PrintAST::PrintInverseRequirements;
PrintAST(Printer, Opts).printRequirementSignature(owner, *this, flags, nullptr);
}
void Requirement::print(raw_ostream &os, const PrintOptions &opts) const {
StreamPrinter printer(os);
PrintAST(printer, opts).printRequirement(*this);

View File

@@ -1286,6 +1286,63 @@ void GenericSignatureImpl::getRequirementsWithInverses(
continue;
}
reqs.push_back(req);
}
}
void RequirementSignature::getRequirementsWithInverses(
ProtocolDecl *owner,
SmallVector<Requirement, 2> &reqs,
SmallVector<InverseRequirement, 2> &inverses) const {
auto &ctx = owner->getASTContext();
if (!SWIFT_ENABLE_EXPERIMENTAL_NONCOPYABLE_GENERICS &&
!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
reqs.append(getRequirements().begin(), getRequirements().end());
return;
}
auto sig = owner->getGenericSignature();
llvm::SmallDenseSet<CanType, 2> assocTypes;
auto visit = [&](Type interfaceType) {
assocTypes.insert(interfaceType->getCanonicalType());
// Any associated type declaration with a superclass bound or concrete type
// does not have an inverse.
if (sig->getSuperclassBound(interfaceType) ||
sig->getConcreteType(interfaceType))
return;
for (auto ip : InvertibleProtocolSet::full()) {
auto *proto = ctx.getProtocol(getKnownProtocolKind(ip));
// If we can derive a conformance to this protocol, then don't add an
// inverse.
if (sig->requiresProtocol(interfaceType, proto))
continue;
// Nothing implies a conformance to this protocol, so record the inverse.
inverses.push_back({interfaceType, proto, SourceLoc()});
}
};
visit(owner->getSelfInterfaceType());
// Record the absence of conformances to invertible protocols.
for (auto assocType : owner->getAssociatedTypeMembers()) {
visit(assocType->getDeclaredInterfaceType());
}
// Filter out explicit conformances to invertible protocols.
for (auto req : getRequirements()) {
if (req.getKind() == RequirementKind::Conformance &&
assocTypes.count(req.getFirstType()->getCanonicalType()) &&
req.getProtocolDecl()->getInvertibleProtocolKind()) {
continue;
}
reqs.push_back(req);
}
}

View File

@@ -3449,28 +3449,18 @@ public:
TypeChecker::inferDefaultWitnesses(PD);
if (Ctx.TypeCheckerOpts.DebugGenericSignatures) {
auto sig = PD->getRequirementSignatureAsGenericSignature();
llvm::errs() << "\n";
llvm::errs() << "Protocol requirement signature:\n";
auto sig = PD->getRequirementSignature();
PD->dumpRef(llvm::errs());
llvm::errs() << "\n";
llvm::errs() << "Requirement signature: ";
PrintOptions Opts;
Opts.ProtocolQualifiedDependentMemberTypes = true;
Opts.PrintInverseRequirements = true;
sig->print(llvm::errs(), Opts);
llvm::errs() << "\n";
llvm::errs() << "Canonical requirement signature: ";
auto canSig =
CanGenericSignature::getCanonical(sig.getGenericParams(),
sig.getRequirements());
canSig->print(llvm::errs(), Opts);
Opts.PrintInverseRequirements =
Ctx.TypeCheckerOpts.DebugInverseRequirements;
sig.print(PD, llvm::errs(), Opts);
llvm::errs() << "\n";
}
dumpGenericSignature(Ctx, PD);
if (!reqSig.getErrors()) {
// Always verify signatures, even if building without asserts.
//

View File

@@ -1,32 +1,23 @@
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
// CHECK: Generic signature: <Self where Self : P1>
// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P1>
// CHECK-LABEL: main.(file).P1@
// CHECK: Requirement signature: <Self>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0>
protocol P1 {
associatedtype A
func f() -> A
}
// Recursion, and where clauses.
// CHECK: Generic signature: <Self where Self : P2>
// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P2>
// CHECK-LABEL: main.(file).P2@
// CHECK: Requirement signature: <Self where Self.[P2]A : P2, Self.[P2]B : P2, Self.[P2]A.[P2]A == Self.[P2]B.[P2]A>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P2]A : P2, τ_0_0.[P2]B : P2, τ_0_0.[P2]A.[P2]A == τ_0_0.[P2]B.[P2]A>
// CHECK-NEXT: Requirement signature: <Self where Self.[P2]A : P2, Self.[P2]B : P2, Self.[P2]A.[P2]A == Self.[P2]B.[P2]A>
protocol P2 {
associatedtype A: P2
associatedtype B: P2 where Self.A.A == Self.B.A
}
// Simpler recursion
// CHECK: Generic signature: <Self where Self : P3>
// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : P3>
// CHECK-LABEL: main.(file).P3@
// CHECK: Requirement signature: <Self where Self.[P3]A : P3>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P3]A : P3>
// CHECK-NEXT: Requirement signature: <Self where Self.[P3]A : P3>
protocol P3 {
associatedtype A: P3
}

View File

@@ -31,6 +31,8 @@ protocol PWorse {
protocol Q1 {}
protocol Q2 {}
// CHECK-LABEL: ExtensionDecl line={{.*}} base=P
// CHECK-NEXT: Generic signature: <Self where Self : P>
extension P {
typealias B = (Q1 & Q2)
}

View File

@@ -23,7 +23,6 @@ class X3 { }
// CHECK-LABEL: .P25a@
// CHECK-NEXT: Requirement signature: <Self where Self.[P25a]A == X24<Self.[P25a]B>, Self.[P25a]B : P20>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P25a]A == X24<τ_0_0.[P25a]B>, τ_0_0.[P25a]B : P20>
protocol P25a {
associatedtype A: P24 // expected-warning{{redundant conformance constraint 'X24<Self.B>' : 'P24'}}
associatedtype B: P20 where A == X24<B>
@@ -31,7 +30,6 @@ protocol P25a {
// CHECK-LABEL: .P25b@
// CHECK-NEXT: Requirement signature: <Self where Self.[P25b]A == X24<Self.[P25b]B>, Self.[P25b]B : P20>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P25b]A == X24<τ_0_0.[P25b]B>, τ_0_0.[P25b]B : P20>
protocol P25b {
associatedtype A
associatedtype B: P20 where A == X24<B>
@@ -39,7 +37,6 @@ protocol P25b {
// CHECK-LABEL: .P27a@
// CHECK-NEXT: Requirement signature: <Self where Self.[P27a]A == X26<Self.[P27a]B>, Self.[P27a]B : X3>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P27a]A == X26<τ_0_0.[P27a]B>, τ_0_0.[P27a]B : X3>
protocol P27a {
associatedtype A: P26 // expected-warning{{redundant conformance constraint 'X26<Self.B>' : 'P26'}}
@@ -48,7 +45,6 @@ protocol P27a {
// CHECK-LABEL: .P27b@
// CHECK-NEXT: Requirement signature: <Self where Self.[P27b]A == X26<Self.[P27b]B>, Self.[P27b]B : X3>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P27b]A == X26<τ_0_0.[P27b]B>, τ_0_0.[P27b]B : X3>
protocol P27b {
associatedtype A
associatedtype B: X3 where A == X26<B>

View File

@@ -282,23 +282,15 @@ struct X22<T, U> {
U == X20<T> { }
}
// CHECK: Generic signature: <Self where Self : P22>
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : P22>
// CHECK: Protocol requirement signature:
// CHECK: .P22@
// CHECK-NEXT: Requirement signature: <Self where Self.[P22]A == X20<Self.[P22]B>, Self.[P22]B : P20>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P22]A == X20<τ_0_0.[P22]B>, τ_0_0.[P22]B : P20>
protocol P22 {
associatedtype A
associatedtype B: P20 where A == X20<B>
}
// CHECK: Generic signature: <Self where Self : P23>
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : P23>
// CHECK: Protocol requirement signature:
// CHECK: .P23@
// CHECK-NEXT: Requirement signature: <Self where Self.[P23]A == X20<Self.[P23]B>, Self.[P23]B : P20>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.[P23]A == X20<τ_0_0.[P23]B>, τ_0_0.[P23]B : P20>
protocol P23 {
associatedtype A
associatedtype B: P20
@@ -332,8 +324,7 @@ struct X28 : P2 {
}
// CHECK-LABEL: .P28@
// CHECK-NEXT: Requirement signature: <Self where Self : P3, Self.[P3]P3Assoc == X28>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : P3, τ_0_0.[P3]P3Assoc == X28>
// CHECK: Requirement signature: <Self where Self : P3, Self.[P3]P3Assoc == X28>
protocol P28: P3 {
typealias P3Assoc = X28 // expected-warning{{typealias overriding associated type}}
}

View File

@@ -11,15 +11,14 @@ protocol IteratorProtocol {
func next() -> Element?
}
// CHECK: requirement_inference_funny_order.(file).LocalArray@
// CHECK: Generic signature: <Element where Element : P1>
// CHECK: ExtensionDecl line={{[0-9]+}} base=LocalArray
// CHECK: Generic signature: <Element where Element : P1, Element : P2>
extension LocalArray where Element : P2 {
static func ==(lhs: Self, rhs: Self) -> Bool {}
}
// CHECK: requirement_inference_funny_order.(file).LocalArray@
// CHECK: Generic signature: <Element where Element : P1>
struct LocalArray<Element : P1>: IteratorProtocol {
func next() -> Element? {}
}

View File

@@ -78,7 +78,7 @@ import NoncopyableGenerics_Misc
// CHECK-MISC-NEXT: public struct ExplicitHello<T> : ~Copyable where T : ~Copyable {
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
// CHECK-MISC-NEXT: extension {{.*}}.ExplicitHello : Swift.Copyable where T : Swift.Copyable {
// CHECK-MISC-NEXT: extension {{.*}}.ExplicitHello : Swift.Copyable {
// CHECK-MISC: #if compiler(>=5.3) && $NoncopyableGenerics
// CHECK-MISC-NEXT: public struct Hello<T> where T : ~Copyable, T : ~Escapable {