Skip to content

Better protocol conformance and substitution map printing #16622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion include/swift/AST/SubstitutionMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,17 @@ class SubstitutionMap {
/// signature nor any replacement types/conformances.
Storage *storage = nullptr;

public:
/// Retrieve the array of replacement types, which line up with the
/// generic parameters.
///
/// Note that the types may be null, for cases where the generic parameter
/// is concrete but hasn't been queried yet.
///
/// Prefer \c getReplacementTypes, this is public for printing purposes.
ArrayRef<Type> getReplacementTypesBuffer() const;

private:
MutableArrayRef<Type> getReplacementTypesBuffer();

/// Retrieve a mutable reference to the buffer of conformances.
Expand Down Expand Up @@ -218,8 +222,12 @@ class SubstitutionMap {
/// Verify that this substitution map is valid.
void verify() const;

/// Whether to dump the full substitution map, or just a minimal useful subset
/// (on a single line).
enum class DumpStyle { Minimal, Full };
/// Dump the contents of this substitution map for debugging purposes.
void dump(llvm::raw_ostream &out) const;
void dump(llvm::raw_ostream &out, DumpStyle style = DumpStyle::Full,
unsigned indent = 0) const;

LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in the debugger");

Expand Down
200 changes: 163 additions & 37 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/ParameterList.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeVisitor.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/QuotedString.h"
#include "swift/Basic/STLExtras.h"
#include "llvm/ADT/APFloat.h"
Expand Down Expand Up @@ -2792,55 +2793,74 @@ void TypeRepr::dump() const {
llvm::errs() << '\n';
}

void ProtocolConformanceRef::dump() const {
dump(llvm::errs());
}

void ProtocolConformanceRef::dump(llvm::raw_ostream &out,
unsigned indent) const {
if (isConcrete()) {
getConcrete()->dump(out, indent);
// Recursive helpers to avoid infinite recursion for recursive protocol
// conformances.
static void dumpProtocolConformanceRec(
const ProtocolConformance *conformance, llvm::raw_ostream &out,
unsigned indent,
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited);

static void dumpSubstitutionMapRec(
SubstitutionMap map, llvm::raw_ostream &out,
SubstitutionMap::DumpStyle style, unsigned indent,
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited);

static void dumpProtocolConformanceRefRec(
const ProtocolConformanceRef conformance, llvm::raw_ostream &out,
unsigned indent,
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited) {
if (conformance.isConcrete()) {
dumpProtocolConformanceRec(conformance.getConcrete(), out, indent, visited);
} else {
out.indent(indent) << "(abstract_conformance protocol="
<< getAbstract()->getName();
<< conformance.getAbstract()->getName();
PrintWithColorRAII(out, ParenthesisColor) << ')';
out << '\n';
}
}

void ProtocolConformance::dump() const {
auto &out = llvm::errs();
dump(out);
out << '\n';
}
static void dumpProtocolConformanceRec(
const ProtocolConformance *conformance, llvm::raw_ostream &out,
unsigned indent,
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited) {
// A recursive conformance shouldn't have its contents printed, or there's
// infinite recursion. (This also avoids printing things that occur multiple
// times in a conformance hierarchy.)
auto shouldPrintDetails = visited.insert(conformance).second;

void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
auto printCommon = [&](StringRef kind) {
out.indent(indent);
PrintWithColorRAII(out, ParenthesisColor) << '(';
out << kind << "_conformance type=" << getType()
<< " protocol=" << getProtocol()->getName();
out << kind << "_conformance type=" << conformance->getType()
<< " protocol=" << conformance->getProtocol()->getName();

if (!shouldPrintDetails)
out << " (details printed above)";
};

switch (getKind()) {
switch (conformance->getKind()) {
case ProtocolConformanceKind::Normal: {
auto normal = cast<NormalProtocolConformance>(this);
auto normal = cast<NormalProtocolConformance>(conformance);

printCommon("normal");
if (!shouldPrintDetails)
break;

// Maybe print information about the conforming context?
if (normal->isLazilyLoaded()) {
out << " lazy";
} else {
forEachTypeWitness(nullptr, [&](const AssociatedTypeDecl *req,
Type ty, const TypeDecl *) -> bool {
out << '\n';
out.indent(indent + 2);
PrintWithColorRAII(out, ParenthesisColor) << '(';
out << "assoc_type req=" << req->getName() << " type=";
PrintWithColorRAII(out, TypeColor) << ty;
PrintWithColorRAII(out, ParenthesisColor) << ')';
return false;
});
normal->forEachTypeWitness(
nullptr,
[&](const AssociatedTypeDecl *req, Type ty,
const TypeDecl *) -> bool {
out << '\n';
out.indent(indent + 2);
PrintWithColorRAII(out, ParenthesisColor) << '(';
out << "assoc_type req=" << req->getName() << " type=";
PrintWithColorRAII(out, TypeColor) << Type(ty->getDesugaredType());
PrintWithColorRAII(out, ParenthesisColor) << ')';
return false;
});
normal->forEachValueWitness(nullptr, [&](const ValueDecl *req,
Witness witness) {
out << '\n';
Expand All @@ -2857,9 +2877,9 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
PrintWithColorRAII(out, ParenthesisColor) << ')';
});

for (auto conformance : normal->getSignatureConformances()) {
for (auto sigConf : normal->getSignatureConformances()) {
out << '\n';
conformance.dump(out, indent + 2);
dumpProtocolConformanceRefRec(sigConf, out, indent + 2, visited);
}
}

Expand All @@ -2872,31 +2892,137 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
}

case ProtocolConformanceKind::Inherited: {
auto conf = cast<InheritedProtocolConformance>(this);
auto conf = cast<InheritedProtocolConformance>(conformance);
printCommon("inherited");
if (!shouldPrintDetails)
break;

out << '\n';
conf->getInheritedConformance()->dump(out, indent + 2);
dumpProtocolConformanceRec(conf->getInheritedConformance(), out, indent + 2,
visited);
break;
}

case ProtocolConformanceKind::Specialized: {
auto conf = cast<SpecializedProtocolConformance>(this);
auto conf = cast<SpecializedProtocolConformance>(conformance);
printCommon("specialized");
if (!shouldPrintDetails)
break;

out << '\n';
dumpSubstitutionMapRec(conf->getSubstitutionMap(), out,
SubstitutionMap::DumpStyle::Full, indent + 2,
visited);
out << '\n';
conf->getSubstitutionMap().dump(out);
for (auto subReq : conf->getConditionalRequirements()) {
out.indent(indent + 2);
subReq.dump(out);
out << '\n';
}
conf->getGenericConformance()->dump(out, indent + 2);
dumpProtocolConformanceRec(conf->getGenericConformance(), out, indent + 2,
visited);
break;
}
}

PrintWithColorRAII(out, ParenthesisColor) << ')';
}

static void dumpSubstitutionMapRec(
SubstitutionMap map, llvm::raw_ostream &out,
SubstitutionMap::DumpStyle style, unsigned indent,
llvm::SmallPtrSetImpl<const ProtocolConformance *> &visited) {
auto *genericSig = map.getGenericSignature();
out.indent(indent);

auto printParen = [&](char p) {
PrintWithColorRAII(out, ParenthesisColor) << p;
};
printParen('(');
SWIFT_DEFER { printParen(')'); };
out << "substitution_map generic_signature=";
if (genericSig == nullptr) {
out << "<nullptr>";
return;
}

genericSig->print(out);
auto genericParams = genericSig->getGenericParams();
auto replacementTypes =
static_cast<const SubstitutionMap &>(map).getReplacementTypesBuffer();
for (unsigned i : indices(genericParams)) {
if (style == SubstitutionMap::DumpStyle::Minimal) {
out << " ";
} else {
out << "\n";
out.indent(indent + 2);
}
printParen('(');
out << "substitution ";
genericParams[i]->print(out);
out << " -> ";
if (replacementTypes[i])
replacementTypes[i]->print(out);
else
out << "<<unresolved concrete type>>";
printParen(')');
}
// A minimal dump doesn't need the details about the conformances, a lot of
// that info can be inferred from the signature.
if (style == SubstitutionMap::DumpStyle::Minimal)
return;

auto conformances = map.getConformances();
for (const auto &req : genericSig->getRequirements()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

out << "\n";
out.indent(indent + 2);
printParen('(');
out << "conformance type=";
req.getFirstType()->print(out);
out << "\n";
dumpProtocolConformanceRefRec(conformances.front(), out, indent + 4,
visited);

printParen(')');
conformances = conformances.slice(1);
}
}

void ProtocolConformanceRef::dump() const {
dump(llvm::errs());
llvm::errs() << '\n';
}

void ProtocolConformanceRef::dump(llvm::raw_ostream &out,
unsigned indent) const {
llvm::SmallPtrSet<const ProtocolConformance *, 8> visited;
dumpProtocolConformanceRefRec(*this, out, indent, visited);
}
void ProtocolConformance::dump() const {
auto &out = llvm::errs();
dump(out);
out << '\n';
}

void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
llvm::SmallPtrSet<const ProtocolConformance *, 8> visited;
dumpProtocolConformanceRec(this, out, indent, visited);
}

void SubstitutionMap::dump(llvm::raw_ostream &out, DumpStyle style,
unsigned indent) const {
llvm::SmallPtrSet<const ProtocolConformance *, 8> visited;
dumpSubstitutionMapRec(*this, out, style, indent, visited);
}

void SubstitutionMap::dump() const {
dump(llvm::errs());
llvm::errs() << "\n";
}

//===----------------------------------------------------------------------===//
// Dumping for Types.
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ConcreteDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void ConcreteDeclRef::dump(raw_ostream &os) {
// If specialized, dump the substitutions.
if (isSpecialized()) {
os << " [with ";
getSubstitutions().dump(os);
getSubstitutions().dump(os, SubstitutionMap::DumpStyle::Minimal);
os << ']';
}
}
Expand Down
42 changes: 0 additions & 42 deletions lib/AST/SubstitutionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,48 +609,6 @@ void SubstitutionMap::verify() const {
#endif
}

void SubstitutionMap::dump(llvm::raw_ostream &out) const {
auto *genericSig = getGenericSignature();
if (genericSig == nullptr) {
out << "Empty substitution map\n";
return;
}
out << "Generic signature: ";
genericSig->print(out);
out << "\n";
out << "Substitutions:\n";
auto genericParams = genericSig->getGenericParams();
auto replacementTypes = getReplacementTypesBuffer();
for (unsigned i : indices(genericParams)) {
out.indent(2);
genericParams[i]->print(out);
out << " -> ";
if (replacementTypes[i])
replacementTypes[i]->print(out);
else
out << "<<unresolved concrete type>>";
out << "\n";
}

out << "\nConformance map:\n";
auto conformances = getConformances();
for (const auto &req : genericSig->getRequirements()) {
if (req.getKind() != RequirementKind::Conformance) continue;

out.indent(2);
req.getFirstType()->print(out);
out << " -> ";
conformances.front().dump(out);
out << "\n";

conformances = conformances.slice(1);
}
}

void SubstitutionMap::dump() const {
return dump(llvm::errs());
}

void SubstitutionMap::profile(llvm::FoldingSetNodeID &id) const {
id.AddPointer(storage);
}
Expand Down
13 changes: 8 additions & 5 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4670,17 +4670,20 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
inferStaticInitializeObjCMetadata(*this, classDecl);
}
}
}

// Check all conformances.
groupChecker.checkAllConformances();

// When requested, print out information about this conformance.
if (Context.LangOpts.DebugGenericSignatures) {
if (Context.LangOpts.DebugGenericSignatures) {
// Now that they're filled out, print out information about the conformances
// here, when requested.
for (auto conformance : conformances) {
dc->dumpContext();
conformance->dump();
}
}

// Check all conformances.
groupChecker.checkAllConformances();

// Catalog all of members of this declaration context that satisfy
// requirements of conformances in this context.
SmallVector<ValueDecl *, 16>
Expand Down
Loading