diff --git a/include/mrdocs/Metadata/Specifiers.hpp b/include/mrdocs/Metadata/Specifiers.hpp index 4b668cb11..de4197a3e 100644 --- a/include/mrdocs/Metadata/Specifiers.hpp +++ b/include/mrdocs/Metadata/Specifiers.hpp @@ -71,37 +71,30 @@ enum class ExplicitKind */ enum class NoexceptKind { - None = 0, - // throw() - ThrowNone, - // throw(type-id-list) - Throw, - // throw(...) (microsoft extension) - ThrowAny, - // __declspec(nothrow) (microsoft extension) - NoThrow, - // noexcept-specifier, no constant-expression - Noexcept, - // noexcept-specifier, constant-expression evaluates to false - NoexceptFalse, - // noexcept-specifier, constant-expression evaluates to true - NoexceptTrue, - // noexcept-specifier, dependent constant-expression - NoexceptDependent, - - // not evaluated yet, for special member function - Unevaluated, - // not instantiated yet - Uninstantiated, - // not parsed yet - Unparsed + /** Potentially-throwing exception specification + */ + False = 0, + /** Non-throwing exception specification + */ + True, + /** Dependent exception specification + */ + Dependent }; // KRYSTIAN FIXME: this needs to be improved (a lot) struct NoexceptInfo { - NoexceptKind Kind = NoexceptKind::None; + /** Whether a noexcept-specifier was user-written. + */ + bool Implicit = true; + + /** The evaluated exception specification. + */ + NoexceptKind Kind = NoexceptKind::False; + /** The operand of the noexcept-specifier, if any. + */ std::string Operand; }; @@ -192,6 +185,21 @@ MRDOCS_DECL dom::String toString(NoexceptKind kind) noexcept; MRDOCS_DECL dom::String toString(ReferenceKind kind) noexcept; MRDOCS_DECL dom::String toString(StorageClassKind kind) noexcept; +/** Convert NoexceptInfo to a string. + + @param resolved If true, the operand is not shown when + the exception specification is non-dependent. + + @param implicit If true, implicit exception specifications + are shown. +*/ +MRDOCS_DECL +dom::String +toString( + NoexceptInfo info, + bool resolved = false, + bool implicit = false); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Type.hpp b/include/mrdocs/Metadata/Type.hpp index 0139a9482..0455091b4 100644 --- a/include/mrdocs/Metadata/Type.hpp +++ b/include/mrdocs/Metadata/Type.hpp @@ -216,7 +216,7 @@ struct FunctionTypeInfo std::vector> ParamTypes; QualifierKind CVQualifiers = QualifierKind::None; ReferenceKind RefQualifier = ReferenceKind::None; - NoexceptKind ExceptionSpec = NoexceptKind::None; + NoexceptKind ExceptionSpec = NoexceptKind::False; TypeInfo* innerType() const noexcept override { diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 430dc5d74..d37565483 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -709,9 +709,15 @@ class ASTVisitor //------------------------------------------------ - std::string - getTypeAsString( - QualType T) + std::string getExprAsString(const Expr* E) + { + std::string result; + llvm::raw_string_ostream stream(result); + E->printPretty(stream, nullptr, context_.getPrintingPolicy()); + return result; + } + + std::string getTypeAsString(QualType T) { return T.getAsString(context_.getPrintingPolicy()); } @@ -1270,6 +1276,59 @@ class ASTVisitor V.getZExtValue()); } + void + buildNoexceptInfo( + NoexceptInfo& I, + ExceptionSpecificationType Kind, + const Expr* E) + { + I.Implicit = Kind == ExceptionSpecificationType::EST_None; + I.Kind = convertToNoexceptKind(Kind); + + if(! E) + return; + + I.Operand = getExprAsString(E); + } + + void + buildNoexceptInfo( + NoexceptInfo& I, + TypeSourceInfo* FTSI) + { + MRDOCS_ASSERT(FTSI && FTSI->getType()->isFunctionProtoType()); + if(auto* FPT = FTSI->getType()->getAs()) + { + buildNoexceptInfo(I, + FPT->getExceptionSpecType(), + FPT->getNoexceptExpr()); + } + } + + void + buildNoexceptInfo( + NoexceptInfo& I, + const FunctionProtoType* FPT) + { + MRDOCS_ASSERT(FPT); + I.Implicit = ! FPT->hasNoexceptExceptionSpec(); + #if 0 + // if the exception specification is unevaluated, + // we just consider it to be dependent + if(FPT->getExceptionSpecType() == + ExceptionSpecificationType::EST_Unevaluated) + I.Kind = NoexceptKind::Dependent; + else + I.Kind = convertToNoexceptKind(FPT->canThrow()); + #endif + I.Kind = convertToNoexceptKind(FPT->getExceptionSpecType()); + + // store the operand, if any + if(Expr* NoexceptExpr = FPT->getNoexceptExpr()) + I.Operand = getExprAsString(NoexceptExpr); + } + + #if 0 void buildNoexceptInfo( NoexceptInfo& I, @@ -1308,6 +1367,7 @@ class ASTVisitor } buildNoexceptInfo(I, Kind, E); } + #endif void buildExprInfo( @@ -1347,6 +1407,16 @@ class ASTVisitor I.Value.emplace(getValue(V)); } + QualType + getDeclaratorType( + const DeclaratorDecl* DD) + { + if(auto* TSI = DD->getTypeSourceInfo(); + TSI && ! TSI->getType().isNull()) + return TSI->getType(); + return DD->getType(); + } + std::unique_ptr buildTemplateParam( const NamedDecl* N) @@ -2190,6 +2260,17 @@ class ASTVisitor addSourceLocation(I, D->getBeginLoc(), D->isThisDeclarationADefinition(), documented); + // KRYSTIAN TODO: move other extraction that requires + // a valid function type here + if(auto FT = getDeclaratorType(D); ! FT.isNull()) + { + const auto* FPT = FT->getAs(); + + buildNoexceptInfo(I.Noexcept, FPT); + + I.specs0.hasTrailingReturn |= FPT->hasTrailingReturn(); + } + // // FunctionDecl // @@ -2204,15 +2285,10 @@ class ASTVisitor // subsumes D->hasAttr() // subsumes D->getType()->getAs()->getNoReturnAttr() I.specs0.hasOverrideAttr |= D->template hasAttr(); - if(auto const* FP = D->getType()->template getAs()) - I.specs0.hasTrailingReturn |= FP->hasTrailingReturn(); I.specs0.constexprKind |= convertToConstexprKind( D->getConstexprKind()); - buildNoexceptInfo(I.Noexcept, D); - // I.specs0.exceptionSpec |= - // convertToNoexceptKind( - // D->getExceptionSpecType()); + I.specs0.overloadedOperator |= convertToOperatorKind( D->getOverloadedOperator()); diff --git a/src/lib/AST/ASTVisitorHelpers.hpp b/src/lib/AST/ASTVisitorHelpers.hpp index f08a67e57..da233e8c7 100644 --- a/src/lib/AST/ASTVisitorHelpers.hpp +++ b/src/lib/AST/ASTVisitorHelpers.hpp @@ -114,20 +114,43 @@ convertToNoexceptKind( { using OldKind = ExceptionSpecificationType; using NewKind = NoexceptKind; + // KRYSTIAN TODO: right now we convert pre-C++17 dynamic exception + // specifications to an (roughly) equivalent noexcept-specifier switch(spec) { - case OldKind::EST_None: return NewKind::None; - case OldKind::EST_DynamicNone: return NewKind::ThrowNone; - case OldKind::EST_Dynamic: return NewKind::Throw; - case OldKind::EST_MSAny: return NewKind::ThrowAny; - case OldKind::EST_NoThrow: return NewKind::NoThrow; - case OldKind::EST_BasicNoexcept: return NewKind::Noexcept; - case OldKind::EST_DependentNoexcept: return NewKind::NoexceptDependent; - case OldKind::EST_NoexceptFalse: return NewKind::NoexceptFalse; - case OldKind::EST_NoexceptTrue: return NewKind::NoexceptTrue; - case OldKind::EST_Unevaluated: return NewKind::Unevaluated; - case OldKind::EST_Uninstantiated: return NewKind::Uninstantiated; - case OldKind::EST_Unparsed: return NewKind::Unparsed; + case OldKind::EST_None: + case OldKind::EST_MSAny: + case OldKind::EST_Unevaluated: + case OldKind::EST_Uninstantiated: + // we *shouldn't* ever encounter an unparsed exception specification, + // assuming that clang is working correctly... + case OldKind::EST_Unparsed: + case OldKind::EST_Dynamic: + case OldKind::EST_NoexceptFalse: + return NewKind::False; + case OldKind::EST_NoThrow: + case OldKind::EST_BasicNoexcept: + case OldKind::EST_NoexceptTrue: + case OldKind::EST_DynamicNone: + return NewKind::True; + case OldKind::EST_DependentNoexcept: + return NewKind::Dependent; + default: + MRDOCS_UNREACHABLE(); + } +} + +NoexceptKind +convertToNoexceptKind( + CanThrowResult kind) +{ + using OldKind = CanThrowResult; + using NewKind = NoexceptKind; + switch(kind) + { + case OldKind::CT_Can: return NewKind::False; + case OldKind::CT_Cannot: return NewKind::True; + case OldKind::CT_Dependent: return NewKind::Dependent; default: MRDOCS_UNREACHABLE(); } diff --git a/src/lib/AST/AnyBlock.hpp b/src/lib/AST/AnyBlock.hpp index d99375f92..37b6c05f3 100644 --- a/src/lib/AST/AnyBlock.hpp +++ b/src/lib/AST/AnyBlock.hpp @@ -191,6 +191,19 @@ decodeRecord( return Error::success(); } +inline +Error +decodeRecord( + Record const& R, + NoexceptInfo& I, + llvm::StringRef Blob) +{ + I.Implicit = R[0]; + I.Kind = static_cast(R[1]); + I.Operand = Blob; + return Error::success(); +} + //------------------------------------------------ struct BitcodeReader::AnyBlock @@ -1314,41 +1327,6 @@ class FunctionParamBlock //------------------------------------------------ -class NoexceptBlock - : public BitcodeReader::AnyBlock -{ - BitcodeReader& br_; - NoexceptInfo& I_; - -public: - NoexceptBlock( - NoexceptInfo& I, - BitcodeReader& br) noexcept - : br_(br) - , I_(I) - { - } - - Error - parseRecord( - Record const& R, - unsigned ID, - llvm::StringRef Blob) override - { - switch(ID) - { - case NOEXCEPT_KIND: - return decodeRecord(R, I_.Kind, Blob); - case NOEXCEPT_OPERAND: - return decodeRecord(R, I_.Operand, Blob); - default: - return AnyBlock::parseRecord(R, ID, Blob); - } - } -}; - -//------------------------------------------------ - template class TopLevelBlock : public BitcodeReader::AnyBlock @@ -1461,6 +1439,8 @@ class FunctionBlock return decodeRecord(R, {&I->specs0.raw, &I->specs1.raw}, Blob); case FUNCTION_CLASS: return decodeRecord(R, I->Class, Blob); + case FUNCTION_NOEXCEPT: + return decodeRecord(R, I->Noexcept, Blob); default: return TopLevelBlock::parseRecord(R, ID, Blob); } @@ -1488,11 +1468,6 @@ class FunctionBlock TemplateBlock B(*I->Template, br_); return br_.readBlock(B, ID); } - case BI_NOEXCEPT_BLOCK_ID: - { - NoexceptBlock B(I->Noexcept, br_); - return br_.readBlock(B, ID); - } default: return TopLevelBlock::readSubBlock(ID); } diff --git a/src/lib/AST/BitcodeIDs.hpp b/src/lib/AST/BitcodeIDs.hpp index 2609c4735..af43b3337 100644 --- a/src/lib/AST/BitcodeIDs.hpp +++ b/src/lib/AST/BitcodeIDs.hpp @@ -79,7 +79,6 @@ enum BlockID BI_TYPEINFO_PARAM_BLOCK_ID, BI_TYPEDEF_BLOCK_ID, BI_VARIABLE_BLOCK_ID, - BI_NOEXCEPT_BLOCK_ID, BI_LAST, BI_FIRST = BI_VERSION_BLOCK_ID }; @@ -118,11 +117,10 @@ enum RecordID FRIEND_SYMBOL, FUNCTION_BITS, FUNCTION_CLASS, + FUNCTION_NOEXCEPT, FUNCTION_PARAM_NAME, FUNCTION_PARAM_DEFAULT, GUIDE_EXPLICIT, - NOEXCEPT_KIND, - NOEXCEPT_OPERAND, JAVADOC_NODE_ADMONISH, JAVADOC_NODE_HREF, JAVADOC_NODE_KIND, diff --git a/src/lib/AST/BitcodeWriter.cpp b/src/lib/AST/BitcodeWriter.cpp index 0ae462b2c..714bd4199 100644 --- a/src/lib/AST/BitcodeWriter.cpp +++ b/src/lib/AST/BitcodeWriter.cpp @@ -177,6 +177,26 @@ static void LocationAbbrev( llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob) }); } +// Assumes that the file will not have more than 65535 lines. +static void NoexceptAbbrev( + std::shared_ptr& Abbrev) +{ + AbbrevGen(Abbrev, { + // NoexceptInfo::Implicit + llvm::BitCodeAbbrevOp( + llvm::BitCodeAbbrevOp::Fixed, + BitCodeConstants::BoolSize), + // NoexceptInfo::Kind + llvm::BitCodeAbbrevOp( + llvm::BitCodeAbbrevOp::Fixed, 2), + // NoexceptInfo::Operand + llvm::BitCodeAbbrevOp( + llvm::BitCodeAbbrevOp::Fixed, + BitCodeConstants::StringLengthSize), + // 5. The string blob + llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob) }); +} + //------------------------------------------------ struct RecordIDDsc @@ -236,7 +256,6 @@ BlockIdNameMap = []() {BI_FRIEND_BLOCK_ID, "FriendBlock"}, {BI_ENUMERATOR_BLOCK_ID, "EnumeratorBlock"}, {BI_VARIABLE_BLOCK_ID, "VarBlock"}, - {BI_NOEXCEPT_BLOCK_ID, "NoexceptBlock"} }; MRDOCS_ASSERT(Inits.size() == BlockIdCount); for (const auto& Init : Inits) @@ -268,11 +287,10 @@ RecordIDNameMap = []() {FRIEND_SYMBOL, {"FriendSymbol", &SymbolIDAbbrev}}, {FUNCTION_BITS, {"Bits", &Integer32ArrayAbbrev}}, {FUNCTION_CLASS, {"FunctionClass", &Integer32Abbrev}}, + {FUNCTION_NOEXCEPT, {"FunctionNoexcept", &NoexceptAbbrev}}, {FUNCTION_PARAM_NAME, {"Name", &StringAbbrev}}, {FUNCTION_PARAM_DEFAULT, {"Default", &StringAbbrev}}, {GUIDE_EXPLICIT, {"Explicit", &Integer32Abbrev}}, - {NOEXCEPT_KIND, {"NoexceptKind", &Integer32Abbrev}}, - {NOEXCEPT_OPERAND, {"NoexceptOperand", &StringAbbrev}}, {INFO_PART_ACCESS, {"InfoAccess", &Integer32Abbrev}}, {INFO_PART_ID, {"InfoID", &SymbolIDAbbrev}}, {INFO_PART_IMPLICIT, {"InfoImplicit", &BoolAbbrev}}, @@ -360,7 +378,7 @@ RecordsByBlock{ FIELD_IS_MUTABLE, FIELD_IS_BITFIELD}}, // FunctionInfo {BI_FUNCTION_BLOCK_ID, - {FUNCTION_BITS, FUNCTION_CLASS}}, + {FUNCTION_BITS, FUNCTION_CLASS, FUNCTION_NOEXCEPT}}, // Param {BI_FUNCTION_PARAM_BLOCK_ID, {FUNCTION_PARAM_NAME, FUNCTION_PARAM_DEFAULT}}, @@ -418,8 +436,6 @@ RecordsByBlock{ {BI_VARIABLE_BLOCK_ID, {VARIABLE_BITS}}, // GuideInfo {BI_GUIDE_BLOCK_ID, {GUIDE_EXPLICIT}}, - // NoexceptInfo - {BI_NOEXCEPT_BLOCK_ID, {NOEXCEPT_KIND, NOEXCEPT_OPERAND}} }; //------------------------------------------------ @@ -691,6 +707,23 @@ emitRecord( Loc.Path + Loc.Filename); } +void +BitcodeWriter:: +emitRecord( + NoexceptInfo const& I, RecordID ID) +{ + MRDOCS_ASSERT(RecordIDNameMap[ID] && "Unknown RecordID."); + MRDOCS_ASSERT(RecordIDNameMap[ID].Abbrev == &NoexceptAbbrev && + "Abbrev type mismatch."); + if (!prepRecordData(ID, true)) + return; + + Record.push_back(I.Implicit); + Record.push_back(to_underlying(I.Kind)); + Record.push_back(I.Operand.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, I.Operand); +} + void BitcodeWriter:: emitRecord( @@ -836,16 +869,6 @@ emitBlock( emitBlock(P.Type); } -void -BitcodeWriter:: -emitBlock( - const NoexceptInfo& I) -{ - StreamSubBlockGuard Block(Stream, BI_NOEXCEPT_BLOCK_ID); - emitRecord(I.Kind, NOEXCEPT_KIND); - emitRecord(I.Operand, NOEXCEPT_OPERAND); -} - void BitcodeWriter:: emitBlock( @@ -861,8 +884,7 @@ emitBlock( emitBlock(I.ReturnType); for (const auto& N : I.Params) emitBlock(N); - if(I.Noexcept.Kind != NoexceptKind::None) - emitBlock(I.Noexcept); + emitRecord(I.Noexcept, FUNCTION_NOEXCEPT); } void diff --git a/src/lib/AST/BitcodeWriter.hpp b/src/lib/AST/BitcodeWriter.hpp index f4888cca3..836847639 100644 --- a/src/lib/AST/BitcodeWriter.hpp +++ b/src/lib/AST/BitcodeWriter.hpp @@ -78,6 +78,7 @@ class BitcodeWriter void emitRecord(SymbolID const& Str, RecordID ID); void emitRecord(llvm::StringRef Str, RecordID ID); void emitRecord(Location const& Loc, RecordID ID); + void emitRecord(NoexceptInfo const& I, RecordID ID); void emitRecord(bool Value, RecordID ID); void emitRecord(std::initializer_list values, RecordID ID); @@ -116,7 +117,6 @@ class BitcodeWriter void emitBlock(FunctionInfo const& I); void emitBlock(GuideInfo const& I); void emitBlock(Param const& I); - void emitBlock(NoexceptInfo const& I); void emitBlock(std::unique_ptr const& jd); void emitBlock(doc::Node const& I); void emitBlock(NamespaceInfo const& I); diff --git a/src/lib/Gen/xml/CXXTags.hpp b/src/lib/Gen/xml/CXXTags.hpp index 65427f494..19786317a 100644 --- a/src/lib/Gen/xml/CXXTags.hpp +++ b/src/lib/Gen/xml/CXXTags.hpp @@ -252,8 +252,10 @@ writeType( if(t.RefQualifier != ReferenceKind::None) attrs.push({"ref-qualifier", toString(t.RefQualifier)}); - if(t.ExceptionSpec != NoexceptKind::None) - attrs.push({"exception-spec", toString(t.ExceptionSpec)}); + // KRYSTIAN TODO: TypeInfo should use ExceptionInfo! + auto exceptSpec = toString(t.ExceptionSpec); + if(! exceptSpec.empty()) + attrs.push({"exception-spec", exceptSpec}); } // ---------------------------------------------------------------- diff --git a/src/lib/Metadata/DomMetadata.cpp b/src/lib/Metadata/DomMetadata.cpp index f13fe7e1c..5957abf91 100644 --- a/src/lib/Metadata/DomMetadata.cpp +++ b/src/lib/Metadata/DomMetadata.cpp @@ -777,6 +777,8 @@ DomInfo::construct() const { "overloadedOperator", I_.specs0.overloadedOperator.get() }, }); + entries.emplace_back("exceptionSpec", toString(I_.Noexcept)); + #if 0 if(I_.Noexcept.Kind != NoexceptKind::None) { dom::String exceptSpec = "noexcept"; @@ -786,6 +788,7 @@ DomInfo::construct() const I_.Noexcept.Operand); entries.emplace_back("exceptionSpec", std::move(exceptSpec)); } + #endif } if constexpr(T::isTypedef()) { diff --git a/src/lib/Metadata/Reduce.cpp b/src/lib/Metadata/Reduce.cpp index fb8d4cdd5..a0727fd8d 100644 --- a/src/lib/Metadata/Reduce.cpp +++ b/src/lib/Metadata/Reduce.cpp @@ -200,7 +200,7 @@ void merge(FunctionInfo& I, FunctionInfo&& Other) I.Template = std::move(Other.Template); I.specs0.raw.value |= Other.specs0.raw.value; I.specs1.raw.value |= Other.specs1.raw.value; - if(I.Noexcept.Kind == NoexceptKind::None) + if(I.Noexcept.Implicit) I.Noexcept = std::move(Other.Noexcept); } diff --git a/src/lib/Metadata/Specifiers.cpp b/src/lib/Metadata/Specifiers.cpp index 1fbfccf96..466319540 100644 --- a/src/lib/Metadata/Specifiers.cpp +++ b/src/lib/Metadata/Specifiers.cpp @@ -70,23 +70,34 @@ dom::String toString(NoexceptKind kind) noexcept { switch(kind) { - case NoexceptKind::None: return ""; - case NoexceptKind::ThrowNone: return "throw()"; - case NoexceptKind::Throw: return "throw"; - case NoexceptKind::ThrowAny: return "throw(...)"; - case NoexceptKind::NoThrow: return "__declspec(nothrow)"; - case NoexceptKind::Noexcept: return "noexcept"; - case NoexceptKind::NoexceptFalse: return "noexcept(false)"; - case NoexceptKind::NoexceptTrue: return "noexcept(true)"; - case NoexceptKind::NoexceptDependent: return "noexcept(expr)"; - case NoexceptKind::Unevaluated: return ""; - case NoexceptKind::Uninstantiated: return ""; - case NoexceptKind::Unparsed: return ""; + case NoexceptKind::False: return ""; + case NoexceptKind::True: return "noexcept"; + case NoexceptKind::Dependent: return "noexcept(...)"; default: MRDOCS_UNREACHABLE(); } } +dom::String +toString( + NoexceptInfo info, + bool resolved, + bool implicit) +{ + if(! implicit && info.Implicit) + return ""; + if(info.Kind == NoexceptKind::Dependent && + info.Operand.empty()) + return ""; + if(info.Kind == NoexceptKind::False && + (resolved || info.Operand.empty())) + return ""; + if(info.Kind == NoexceptKind::True && + (resolved || info.Operand.empty())) + return "noexcept"; + return fmt::format("noexcept({})", info.Operand); +} + dom::String toString(ReferenceKind kind) noexcept { switch(kind) diff --git a/src/lib/Metadata/Type.cpp b/src/lib/Metadata/Type.cpp index 1e166c106..a4abb7486 100644 --- a/src/lib/Metadata/Type.cpp +++ b/src/lib/Metadata/Type.cpp @@ -251,7 +251,7 @@ operator()( if(t.RefQualifier != ReferenceKind::None) write(' ', toString(t.RefQualifier)); - if(t.ExceptionSpec != NoexceptKind::None) + if(t.ExceptionSpec != NoexceptKind::False) write(' ', toString(t.ExceptionSpec)); }