Skip to content

[clang-doc] add support for concepts #144430

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 1 commit into from
Jun 21, 2025
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
73 changes: 73 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ static llvm::Error decodeRecord(const Record &R, InfoType &Field,
case InfoType::IT_default:
case InfoType::IT_enum:
case InfoType::IT_typedef:
case InfoType::IT_concept:
Field = IT;
return llvm::Error::success();
}
Expand All @@ -108,6 +109,7 @@ static llvm::Error decodeRecord(const Record &R, FieldId &Field,
case FieldId::F_type:
case FieldId::F_child_namespace:
case FieldId::F_child_record:
case FieldId::F_concept:
case FieldId::F_default:
Field = F;
return llvm::Error::success();
Expand Down Expand Up @@ -391,6 +393,29 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
"invalid field for TemplateParamInfo");
}

static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, ConceptInfo *I) {
switch (ID) {
case CONCEPT_USR:
return decodeRecord(R, I->USR, Blob);
case CONCEPT_NAME:
return decodeRecord(R, I->Name, Blob);
case CONCEPT_IS_TYPE:
return decodeRecord(R, I->IsType, Blob);
case CONCEPT_CONSTRAINT_EXPRESSION:
return decodeRecord(R, I->ConstraintExpression, Blob);
}
llvm_unreachable("invalid field for ConceptInfo");
}

static llvm::Error parseRecord(const Record &R, unsigned ID,
llvm::StringRef Blob, ConstraintInfo *I) {
if (ID == CONSTRAINT_EXPRESSION)
return decodeRecord(R, I->ConstraintExpr, Blob);
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for ConstraintInfo");
}

template <typename T> static llvm::Expected<CommentInfo *> getCommentInfo(T I) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain CommentInfo");
Expand Down Expand Up @@ -429,6 +454,10 @@ template <> llvm::Expected<CommentInfo *> getCommentInfo(CommentInfo *I) {
return I->Children.back().get();
}

template <> llvm::Expected<CommentInfo *> getCommentInfo(ConceptInfo *I) {
return &I->Description.emplace_back();
}

// When readSubBlock encounters a TypeInfo sub-block, it calls addTypeInfo on
// the parent block to set it. The template specializations define what to do
// for each supported parent block.
Expand Down Expand Up @@ -584,6 +613,17 @@ template <> llvm::Error addReference(RecordInfo *I, Reference &&R, FieldId F) {
}
}

template <>
llvm::Error addReference(ConstraintInfo *I, Reference &&R, FieldId F) {
if (F == FieldId::F_concept) {
I->ConceptRef = std::move(R);
return llvm::Error::success();
}
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"ConstraintInfo cannot contain this Reference");
}

template <typename T, typename ChildInfoType>
static void addChild(T I, ChildInfoType &&R) {
llvm::errs() << "invalid child type for info";
Expand All @@ -600,6 +640,9 @@ template <> void addChild(NamespaceInfo *I, EnumInfo &&R) {
template <> void addChild(NamespaceInfo *I, TypedefInfo &&R) {
I->Children.Typedefs.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, ConceptInfo &&R) {
I->Children.Concepts.emplace_back(std::move(R));
}

// Record children:
template <> void addChild(RecordInfo *I, FunctionInfo &&R) {
Expand Down Expand Up @@ -649,6 +692,9 @@ template <> void addTemplate(RecordInfo *I, TemplateInfo &&P) {
template <> void addTemplate(FunctionInfo *I, TemplateInfo &&P) {
I->Template.emplace(std::move(P));
}
template <> void addTemplate(ConceptInfo *I, TemplateInfo &&P) {
I->Template = std::move(P);
}
Comment on lines +695 to +697
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not something you need to fix here, but dang do we require a lot of boiler-plate changes to the APIs. It's almost like we're not holding templates in the normal way...


// Template specializations go only into template records.
template <typename T>
Expand All @@ -662,6 +708,14 @@ void addTemplateSpecialization(TemplateInfo *I,
I->Specialization.emplace(std::move(TSI));
}

template <typename T> static void addConstraint(T I, ConstraintInfo &&C) {
llvm::errs() << "invalid container for constraint info";
exit(1);
}
template <> void addConstraint(TemplateInfo *I, ConstraintInfo &&C) {
I->Constraints.emplace_back(std::move(C));
}

// Read records from bitcode into a given info.
template <typename T>
llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
Expand Down Expand Up @@ -716,6 +770,8 @@ llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) {
}
}

// TODO: Create a helper that can receive a function to reduce repetition for
// most blocks.
template <typename T>
llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
llvm::TimeTraceScope("Reducing infos", "readSubBlock");
Expand Down Expand Up @@ -817,6 +873,20 @@ llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
addChild(I, std::move(TI));
return llvm::Error::success();
}
case BI_CONSTRAINT_BLOCK_ID: {
ConstraintInfo CI;
if (auto Err = readBlock(ID, &CI))
return Err;
addConstraint(I, std::move(CI));
return llvm::Error::success();
}
case BI_CONCEPT_BLOCK_ID: {
ConceptInfo CI;
if (auto Err = readBlock(ID, &CI))
return Err;
addChild(I, std::move(CI));
return llvm::Error::success();
}
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid subblock type");
Expand Down Expand Up @@ -922,6 +992,8 @@ ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
return createInfo<EnumInfo>(ID);
case BI_TYPEDEF_BLOCK_ID:
return createInfo<TypedefInfo>(ID);
case BI_CONCEPT_BLOCK_ID:
return createInfo<ConceptInfo>(ID);
case BI_FUNCTION_BLOCK_ID:
return createInfo<FunctionInfo>(ID);
default:
Expand Down Expand Up @@ -962,6 +1034,7 @@ ClangDocBitcodeReader::readBitcode() {
case BI_RECORD_BLOCK_ID:
case BI_ENUM_BLOCK_ID:
case BI_TYPEDEF_BLOCK_ID:
case BI_CONCEPT_BLOCK_ID:
case BI_FUNCTION_BLOCK_ID: {
auto InfoOrErr = readBlockToInfo(ID);
if (!InfoOrErr)
Expand Down
44 changes: 41 additions & 3 deletions clang-tools-extra/clang-doc/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
{BI_REFERENCE_BLOCK_ID, "ReferenceBlock"},
{BI_TEMPLATE_BLOCK_ID, "TemplateBlock"},
{BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, "TemplateSpecializationBlock"},
{BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"}};
{BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"},
{BI_CONSTRAINT_BLOCK_ID, "ConstraintBlock"},
{BI_CONCEPT_BLOCK_ID, "ConceptBlock"}};
assert(Inits.size() == BlockIdCount);
for (const auto &Init : Inits)
BlockIdNameMap[Init.first] = Init.second;
Expand Down Expand Up @@ -205,7 +207,13 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{TYPEDEF_USR, {"USR", &genSymbolIdAbbrev}},
{TYPEDEF_NAME, {"Name", &genStringAbbrev}},
{TYPEDEF_DEFLOCATION, {"DefLocation", &genLocationAbbrev}},
{TYPEDEF_IS_USING, {"IsUsing", &genBoolAbbrev}}};
{TYPEDEF_IS_USING, {"IsUsing", &genBoolAbbrev}},
{CONCEPT_USR, {"USR", &genSymbolIdAbbrev}},
{CONCEPT_NAME, {"Name", &genStringAbbrev}},
{CONCEPT_IS_TYPE, {"IsType", &genBoolAbbrev}},
{CONCEPT_CONSTRAINT_EXPRESSION,
{"ConstraintExpression", &genStringAbbrev}},
{CONSTRAINT_EXPRESSION, {"Expression", &genStringAbbrev}}};
assert(Inits.size() == RecordIdCount);
for (const auto &Init : Inits) {
RecordIdNameMap[Init.first] = Init.second;
Expand Down Expand Up @@ -263,7 +271,13 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
// Template Blocks.
{BI_TEMPLATE_BLOCK_ID, {}},
{BI_TEMPLATE_PARAM_BLOCK_ID, {TEMPLATE_PARAM_CONTENTS}},
{BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, {TEMPLATE_SPECIALIZATION_OF}}};
{BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, {TEMPLATE_SPECIALIZATION_OF}},
// Concept Block
{BI_CONCEPT_BLOCK_ID,
{CONCEPT_USR, CONCEPT_NAME, CONCEPT_IS_TYPE,
CONCEPT_CONSTRAINT_EXPRESSION}},
// Constraint Block
{BI_CONSTRAINT_BLOCK_ID, {CONSTRAINT_EXPRESSION}}};

// AbbreviationMap

Expand Down Expand Up @@ -524,6 +538,8 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
emitBlock(C);
for (const auto &C : I.Children.Typedefs)
emitBlock(C);
for (const auto &C : I.Children.Concepts)
emitBlock(C);
}

void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) {
Expand Down Expand Up @@ -627,12 +643,25 @@ void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
emitBlock(*I.Template);
}

void ClangDocBitcodeWriter::emitBlock(const ConceptInfo &I) {
StreamSubBlockGuard Block(Stream, BI_CONCEPT_BLOCK_ID);
emitRecord(I.USR, CONCEPT_USR);
emitRecord(I.Name, CONCEPT_NAME);
for (const auto &CI : I.Description)
emitBlock(CI);
emitRecord(I.IsType, CONCEPT_IS_TYPE);
emitRecord(I.ConstraintExpression, CONCEPT_CONSTRAINT_EXPRESSION);
emitBlock(I.Template);
}

void ClangDocBitcodeWriter::emitBlock(const TemplateInfo &T) {
StreamSubBlockGuard Block(Stream, BI_TEMPLATE_BLOCK_ID);
for (const auto &P : T.Params)
emitBlock(P);
if (T.Specialization)
emitBlock(*T.Specialization);
for (const auto &C : T.Constraints)
emitBlock(C);
}

void ClangDocBitcodeWriter::emitBlock(const TemplateSpecializationInfo &T) {
Expand All @@ -647,6 +676,12 @@ void ClangDocBitcodeWriter::emitBlock(const TemplateParamInfo &T) {
emitRecord(T.Contents, TEMPLATE_PARAM_CONTENTS);
}

void ClangDocBitcodeWriter::emitBlock(const ConstraintInfo &C) {
StreamSubBlockGuard Block(Stream, BI_CONSTRAINT_BLOCK_ID);
emitRecord(C.ConstraintExpr, CONSTRAINT_EXPRESSION);
emitBlock(C.ConceptRef, FieldId::F_concept);
}

bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
switch (I->IT) {
case InfoType::IT_namespace:
Expand All @@ -664,6 +699,9 @@ bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
case InfoType::IT_typedef:
emitBlock(*static_cast<clang::doc::TypedefInfo *>(I));
break;
case InfoType::IT_concept:
emitBlock(*static_cast<clang::doc::ConceptInfo *>(I));
break;
case InfoType::IT_default:
llvm::errs() << "Unexpected info, unable to write.\n";
return true;
Expand Down
12 changes: 11 additions & 1 deletion clang-tools-extra/clang-doc/BitcodeWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ enum BlockId {
BI_TEMPLATE_BLOCK_ID,
BI_TEMPLATE_SPECIALIZATION_BLOCK_ID,
BI_TEMPLATE_PARAM_BLOCK_ID,
BI_CONSTRAINT_BLOCK_ID,
BI_TYPEDEF_BLOCK_ID,
BI_CONCEPT_BLOCK_ID,
BI_LAST,
BI_FIRST = BI_VERSION_BLOCK_ID
};
Expand Down Expand Up @@ -135,6 +137,11 @@ enum RecordId {
TYPEDEF_NAME,
TYPEDEF_DEFLOCATION,
TYPEDEF_IS_USING,
CONCEPT_USR,
CONCEPT_NAME,
CONCEPT_IS_TYPE,
CONCEPT_CONSTRAINT_EXPRESSION,
CONSTRAINT_EXPRESSION,
RI_LAST,
RI_FIRST = VERSION
};
Expand All @@ -150,7 +157,8 @@ enum class FieldId {
F_vparent,
F_type,
F_child_namespace,
F_child_record
F_child_record,
F_concept
};

class ClangDocBitcodeWriter {
Expand Down Expand Up @@ -179,6 +187,8 @@ class ClangDocBitcodeWriter {
void emitBlock(const TemplateInfo &T);
void emitBlock(const TemplateSpecializationInfo &T);
void emitBlock(const TemplateParamInfo &T);
void emitBlock(const ConceptInfo &T);
void emitBlock(const ConstraintInfo &T);
void emitBlock(const Reference &B, FieldId F);

private:
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,8 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
MainContentNodes =
genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
break;
case InfoType::IT_concept:
break;
case InfoType::IT_default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected info type");
Expand All @@ -1011,6 +1013,8 @@ static std::string getRefType(InfoType IT) {
return "enum";
case InfoType::IT_typedef:
return "typedef";
case InfoType::IT_concept:
return "concept";
}
llvm_unreachable("Unknown InfoType");
}
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,8 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
case InfoType::IT_typedef:
OS << "IT_typedef\n";
break;
case InfoType::IT_concept:
break;
case InfoType::IT_default:
return createStringError(inconvertibleErrorCode(), "unexpected InfoType");
}
Expand Down
Loading