https://github.com/bolshakov-a updated https://github.com/llvm/llvm-project/pull/78041
>From 9bef9d5dd970f1af653d5b10d0a062d96a69ad01 Mon Sep 17 00:00:00 2001 From: Andrey Ali Khan Bolshakov <bo...@cmp1.home> Date: Sun, 6 Aug 2023 19:38:23 +0300 Subject: [PATCH] [c++20] P1907R1: Support for generalized non-type template arguments of scalar type. Previously committed as 9e08e51a20d0d2b1c5724bb17e969d036fced4cd, and reverted because a dependency commit was reverted, then committed again as 4b574008aef5a7235c1f894ab065fe300d26e786 and reverted again because "dependency commit" 5a391d38ac6c561ba908334d427f26124ed9132e was reverted. But it doesn't seem that 5a391d38ac6c was a real dependency for this. This commit incorporates 4b574008aef5a7235c1f894ab065fe300d26e786 and 18e093faf726d15f210ab4917142beec51848258 by Richard Smith, with some minor fixes, most notably: - `UncommonValue` renamed to `StructuralValue` - `VK_PRValue` instead of `VK_RValue` as default kind in lvalue and member pointer handling branch in `BuildExpressionFromNonTypeTemplateArgumentValue`; - handling of `StructuralValue` in `IsTypeDeclaredInsideVisitor`; - filling in `SugaredConverted` along with `CanonicalConverted` parameter in `Sema::CheckTemplateArgument`; - minor cleanup in `TemplateInstantiator::transformNonTypeTemplateParmRef`; - `TemplateArgument` constructors refactored; - `ODRHash` calculation for `UncommonValue`; - USR generation for `UncommonValue`; - more correct MS compatibility mangling algorithm (tested on MSVC ver. 19.35; toolset ver. 143); - IR emitting fixed on using a subobject as a template argument when the corresponding template parameter is used in an lvalue context; - `noundef` attribute and opaque pointers in `template-arguments` test; - analysis for C++17 mode is turned off for templates in `warn-bool-conversion` test; in C++17 and C++20 mode, array reference used as a template argument of pointer type produces template argument of UncommonValue type, and `BuildExpressionFromNonTypeTemplateArgumentValue` makes `OpaqueValueExpr` for it, and `DiagnoseAlwaysNonNullPointer` cannot see through it; despite of "These cases should not warn" comment, I'm not sure about correct behavior; I'd expect a suggestion to replace `if` by `if constexpr`; - `temp.arg.nontype/p1.cpp` and `dr18xx.cpp` tests fixed. --- clang-tools-extra/clangd/DumpAST.cpp | 1 + clang-tools-extra/clangd/FindTarget.cpp | 1 + clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/AST/ODRHash.h | 3 + clang/include/clang/AST/PropertiesBase.td | 14 ++ clang/include/clang/AST/RecursiveASTVisitor.h | 2 + .../clang/AST/TemplateArgumentVisitor.h | 2 + clang/include/clang/AST/TemplateBase.h | 86 ++++--- .../clang/Basic/DiagnosticSemaKinds.td | 5 - clang/include/clang/Sema/Sema.h | 4 +- clang/lib/AST/ASTContext.cpp | 5 + clang/lib/AST/ASTImporter.cpp | 13 ++ clang/lib/AST/ASTStructuralEquivalence.cpp | 3 + clang/lib/AST/Decl.cpp | 4 + clang/lib/AST/ItaniumMangle.cpp | 36 ++- clang/lib/AST/MicrosoftMangle.cpp | 78 +++++-- clang/lib/AST/ODRHash.cpp | 67 ++++++ clang/lib/AST/StmtProfile.cpp | 6 + clang/lib/AST/TemplateBase.cpp | 113 ++++++++- clang/lib/AST/TypeLoc.cpp | 1 + clang/lib/CodeGen/CGDebugInfo.cpp | 10 + clang/lib/CodeGen/CGExpr.cpp | 12 +- clang/lib/Index/USRGeneration.cpp | 10 + clang/lib/Sema/SemaLookup.cpp | 1 + clang/lib/Sema/SemaOverload.cpp | 10 +- clang/lib/Sema/SemaTemplate.cpp | 218 +++++++++++------- clang/lib/Sema/SemaTemplateDeduction.cpp | 64 +++-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 14 +- clang/lib/Sema/SemaTemplateVariadic.cpp | 2 + clang/lib/Sema/TreeTransform.h | 12 +- clang/lib/Serialization/ASTReader.cpp | 1 + clang/lib/Serialization/ASTWriter.cpp | 1 + clang/test/CXX/drs/dr18xx.cpp | 4 +- .../CXX/temp/temp.arg/temp.arg.nontype/p1.cpp | 4 +- clang/test/CodeGenCXX/mangle-ms-templates.cpp | 48 ++++ clang/test/CodeGenCXX/mangle-template.cpp | 40 +++- clang/test/CodeGenCXX/template-arguments.cpp | 113 +++++++++ .../Index/USR/structural-value-tpl-arg.cpp | 23 ++ clang/test/Modules/odr_hash.cpp | 193 +++++++++++++++- clang/test/SemaCXX/warn-bool-conversion.cpp | 2 + .../SemaTemplate/temp_arg_nontype_cxx1z.cpp | 40 ++-- .../SemaTemplate/temp_arg_nontype_cxx20.cpp | 40 ++-- clang/tools/libclang/CIndex.cpp | 5 + clang/tools/libclang/CXCursor.cpp | 3 + clang/www/cxx_status.html | 18 +- lldb/include/lldb/lldb-enumerations.h | 1 + .../TypeSystem/Clang/TypeSystemClang.cpp | 3 + 47 files changed, 1108 insertions(+), 230 deletions(-) create mode 100644 clang/test/CodeGenCXX/template-arguments.cpp create mode 100644 clang/test/Index/USR/structural-value-tpl-arg.cpp diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index b0cec65c39fa31..9a525efb938e8d 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -143,6 +143,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> { TEMPLATE_ARGUMENT_KIND(Declaration); TEMPLATE_ARGUMENT_KIND(Template); TEMPLATE_ARGUMENT_KIND(TemplateExpansion); + TEMPLATE_ARGUMENT_KIND(StructuralValue); #undef TEMPLATE_ARGUMENT_KIND } llvm_unreachable("Unhandled ArgKind enum"); diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 839cf6332fe8b0..a4a261030d399b 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -1032,6 +1032,7 @@ class ExplicitReferenceCollector case TemplateArgument::Pack: case TemplateArgument::Type: case TemplateArgument::Expression: + case TemplateArgument::StructuralValue: break; // Handled by VisitType and VisitExpression. }; return RecursiveASTVisitor::TraverseTemplateArgumentLoc(A); diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3cbce1be159437..57c444fd95be1b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -180,6 +180,8 @@ C++ Language Changes C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented `P1907R1 <https://wg21.link/P1907R1>` which extends allowed non-type template argument + kinds with e.g. floating point values and pointers and references to subobjects. C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h index cedf644520fc32..a1caa6d39a87c3 100644 --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -25,6 +25,7 @@ namespace clang { +class APValue; class Decl; class IdentifierInfo; class NestedNameSpecifier; @@ -101,6 +102,8 @@ class ODRHash { // Save booleans until the end to lower the size of data to process. void AddBoolean(bool value); + void AddStructuralValue(const APValue &); + static bool isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent); private: diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index d86c4eba6a2251..0270c086d06b6a 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -808,6 +808,20 @@ let Class = PropertyTypeCase<TemplateArgument, "Integral"> in { return TemplateArgument(ctx, value, type, isDefaulted); }]>; } +let Class = PropertyTypeCase<TemplateArgument, "StructuralValue"> in { + def : Property<"value", APValue> { + let Read = [{ node.getAsStructuralValue() }]; + } + def : Property<"type", QualType> { + let Read = [{ node.getStructuralValueType() }]; + } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } + def : Creator<[{ + return TemplateArgument(ctx, type, value, isDefaulted); + }]>; +} let Class = PropertyTypeCase<TemplateArgument, "Template"> in { def : Property<"name", TemplateName> { let Read = [{ node.getAsTemplateOrTemplatePattern() }]; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 8f2714e142bbe3..2aee6a947141b6 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -850,6 +850,7 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateArgument( case TemplateArgument::Declaration: case TemplateArgument::Integral: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: return true; case TemplateArgument::Type: @@ -882,6 +883,7 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLoc( case TemplateArgument::Declaration: case TemplateArgument::Integral: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: return true; case TemplateArgument::Type: { diff --git a/clang/include/clang/AST/TemplateArgumentVisitor.h b/clang/include/clang/AST/TemplateArgumentVisitor.h index 190aa97adf4551..cf0d3220158063 100644 --- a/clang/include/clang/AST/TemplateArgumentVisitor.h +++ b/clang/include/clang/AST/TemplateArgumentVisitor.h @@ -37,6 +37,7 @@ class Base { DISPATCH(Declaration); DISPATCH(NullPtr); DISPATCH(Integral); + DISPATCH(StructuralValue); DISPATCH(Template); DISPATCH(TemplateExpansion); DISPATCH(Expression); @@ -59,6 +60,7 @@ class Base { VISIT_METHOD(Declaration); VISIT_METHOD(NullPtr); VISIT_METHOD(Integral); + VISIT_METHOD(StructuralValue); VISIT_METHOD(Template); VISIT_METHOD(TemplateExpansion); VISIT_METHOD(Expression); diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index b7cd71f17c9442..fea2c8ccfee675 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -50,6 +50,7 @@ template <> struct PointerLikeTypeTraits<clang::Expr *> { namespace clang { +class APValue; class ASTContext; class Expr; struct PrintingPolicy; @@ -80,6 +81,13 @@ class TemplateArgument { /// that was provided for an integral non-type template parameter. Integral, + /// The template argument is a non-type template argument that can't be + /// represented by the special-case Declaration, NullPtr, or Integral + /// forms. These values are only ever produced by constant evaluation, + /// so cannot be dependent. + /// TODO: merge Declaration, NullPtr and Integral into this? + StructuralValue, + /// The template argument is a template name that was provided for a /// template template parameter. Template, @@ -130,6 +138,14 @@ class TemplateArgument { }; void *Type; }; + struct V { + LLVM_PREFERRED_TYPE(ArgKind) + unsigned Kind : 31; + LLVM_PREFERRED_TYPE(bool) + unsigned IsDefaulted : 1; + APValue *Value; + void *Type; + }; struct A { LLVM_PREFERRED_TYPE(ArgKind) unsigned Kind : 31; @@ -156,11 +172,19 @@ class TemplateArgument { union { struct DA DeclArg; struct I Integer; + struct V Value; struct A Args; struct TA TemplateArg; struct TV TypeOrValue; }; + void initFromType(QualType T, bool IsNullPtr, bool IsDefaulted); + void initFromDeclaration(ValueDecl *D, QualType QT, bool IsDefaulted); + void initFromIntegral(const ASTContext &Ctx, const llvm::APSInt &Value, + QualType Type, bool IsDefaulted); + void initFromStructural(const ASTContext &Ctx, QualType Type, + const APValue &V, bool IsDefaulted); + public: /// Construct an empty, invalid template argument. constexpr TemplateArgument() : TypeOrValue({Null, 0, /* IsDefaulted */ 0}) {} @@ -168,25 +192,22 @@ class TemplateArgument { /// Construct a template type argument. TemplateArgument(QualType T, bool isNullPtr = false, bool IsDefaulted = false) { - TypeOrValue.Kind = isNullPtr ? NullPtr : Type; - TypeOrValue.IsDefaulted = IsDefaulted; - TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr()); + initFromType(T, isNullPtr, IsDefaulted); } - /// Construct a template argument that refers to a - /// declaration, which is either an external declaration or a - /// template declaration. + /// Construct a template argument that refers to a (non-dependent) + /// declaration. TemplateArgument(ValueDecl *D, QualType QT, bool IsDefaulted = false) { - assert(D && "Expected decl"); - DeclArg.Kind = Declaration; - DeclArg.IsDefaulted = IsDefaulted; - DeclArg.QT = QT.getAsOpaquePtr(); - DeclArg.D = D; + initFromDeclaration(D, QT, IsDefaulted); } /// Construct an integral constant template argument. The memory to /// store the value is allocated with Ctx. - TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type, + TemplateArgument(const ASTContext &Ctx, const llvm::APSInt &Value, + QualType Type, bool IsDefaulted = false); + + /// Construct a template argument from an arbitrary constant value. + TemplateArgument(const ASTContext &Ctx, QualType Type, const APValue &Value, bool IsDefaulted = false); /// Construct an integral constant template argument with the same @@ -297,7 +318,7 @@ class TemplateArgument { /// Retrieve the type for a type template argument. QualType getAsType() const { assert(getKind() == Type && "Unexpected kind"); - return QualType::getFromOpaquePtr(reinterpret_cast<void*>(TypeOrValue.V)); + return QualType::getFromOpaquePtr(reinterpret_cast<void *>(TypeOrValue.V)); } /// Retrieve the declaration for a declaration non-type @@ -315,7 +336,7 @@ class TemplateArgument { /// Retrieve the type for null non-type template argument. QualType getNullPtrType() const { assert(getKind() == NullPtr && "Unexpected kind"); - return QualType::getFromOpaquePtr(reinterpret_cast<void*>(TypeOrValue.V)); + return QualType::getFromOpaquePtr(reinterpret_cast<void *>(TypeOrValue.V)); } /// Retrieve the template name for a template name argument. @@ -371,6 +392,14 @@ class TemplateArgument { /// default template parameter. bool getIsDefaulted() const { return (bool)TypeOrValue.IsDefaulted; } + /// Get the value of a StructuralValue. + const APValue &getAsStructuralValue() const { return *Value.Value; } + + /// Get the type of a StructuralValue. + QualType getStructuralValueType() const { + return QualType::getFromOpaquePtr(Value.Type); + } + /// If this is a non-type template argument, get its type. Otherwise, /// returns a null QualType. QualType getNonTypeTemplateArgumentType() const; @@ -516,6 +545,7 @@ class TemplateArgumentLoc { assert(Argument.getKind() == TemplateArgument::NullPtr || Argument.getKind() == TemplateArgument::Integral || Argument.getKind() == TemplateArgument::Declaration || + Argument.getKind() == TemplateArgument::StructuralValue || Argument.getKind() == TemplateArgument::Expression); } @@ -541,13 +571,9 @@ class TemplateArgumentLoc { /// - Fetches the full source range of the argument. SourceRange getSourceRange() const LLVM_READONLY; - const TemplateArgument &getArgument() const { - return Argument; - } + const TemplateArgument &getArgument() const { return Argument; } - TemplateArgumentLocInfo getLocInfo() const { - return LocInfo; - } + TemplateArgumentLocInfo getLocInfo() const { return LocInfo; } TypeSourceInfo *getTypeSourceInfo() const { if (Argument.getKind() != TemplateArgument::Type) @@ -575,6 +601,11 @@ class TemplateArgumentLoc { return LocInfo.getAsExpr(); } + Expr *getSourceStructuralValueExpression() const { + assert(Argument.getKind() == TemplateArgument::StructuralValue); + return LocInfo.getAsExpr(); + } + NestedNameSpecifierLoc getTemplateQualifierLoc() const { if (Argument.getKind() != TemplateArgument::Template && Argument.getKind() != TemplateArgument::TemplateExpansion) @@ -606,8 +637,7 @@ class TemplateArgumentListInfo { public: TemplateArgumentListInfo() = default; - TemplateArgumentListInfo(SourceLocation LAngleLoc, - SourceLocation RAngleLoc) + TemplateArgumentListInfo(SourceLocation LAngleLoc, SourceLocation RAngleLoc) : LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc) {} // This can leak if used in an AST node, use ASTTemplateArgumentListInfo @@ -626,21 +656,15 @@ class TemplateArgumentListInfo { return Arguments.data(); } - llvm::ArrayRef<TemplateArgumentLoc> arguments() const { - return Arguments; - } + llvm::ArrayRef<TemplateArgumentLoc> arguments() const { return Arguments; } const TemplateArgumentLoc &operator[](unsigned I) const { return Arguments[I]; } - TemplateArgumentLoc &operator[](unsigned I) { - return Arguments[I]; - } + TemplateArgumentLoc &operator[](unsigned I) { return Arguments[I]; } - void addArgument(const TemplateArgumentLoc &Loc) { - Arguments.push_back(Loc); - } + void addArgument(const TemplateArgumentLoc &Loc) { Arguments.push_back(Loc); } }; /// Represents an explicit template argument list in C++, e.g., diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1a79892e40030a..8c74d12e674624 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5138,8 +5138,6 @@ def err_non_type_template_arg_subobject : Error< "non-type template argument refers to subobject '%0'">; def err_non_type_template_arg_addr_label_diff : Error< "template argument / label address difference / what did you expect?">; -def err_non_type_template_arg_unsupported : Error< - "sorry, non-type template argument of type %0 is not yet supported">; def err_template_arg_not_convertible : Error< "non-type template argument of type %0 cannot be converted to a value " "of type %1">; @@ -5191,9 +5189,6 @@ def err_template_arg_not_object_or_func : Error< "non-type template argument does not refer to an object or function">; def err_template_arg_not_pointer_to_member_form : Error< "non-type template argument is not a pointer to member constant">; -def err_template_arg_member_ptr_base_derived_not_supported : Error< - "sorry, non-type template argument of pointer-to-member type %1 that refers " - "to member %q0 of a different class is not supported yet">; def err_template_arg_invalid : Error< "non-type template argument '%0' is invalid">; def ext_template_arg_extra_parens : ExtWarn< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index cf2d4fbe6d3ba1..a3881709263daa 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8569,8 +8569,8 @@ class Sema final { QualType ParamType, SourceLocation Loc); ExprResult - BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, - SourceLocation Loc); + BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, + SourceLocation Loc); /// Enumeration describing how template parameter lists are compared /// for equality. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index d9cefcaa84d7e5..2df986c8541057 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6753,6 +6753,11 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { case TemplateArgument::Integral: return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); + case TemplateArgument::StructuralValue: + return TemplateArgument(*this, + getCanonicalType(Arg.getStructuralValueType()), + Arg.getAsStructuralValue()); + case TemplateArgument::Type: return TemplateArgument(getCanonicalType(Arg.getAsType()), /*isNullPtr*/ false, Arg.getIsDefaulted()); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index b762d6a4cd3800..8c279dd1eb048b 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -823,6 +823,17 @@ ASTNodeImporter::import(const TemplateArgument &From) { From.getIsDefaulted()); } + case TemplateArgument::StructuralValue: { + ExpectedType ToTypeOrErr = import(From.getStructuralValueType()); + if (!ToTypeOrErr) + return ToTypeOrErr.takeError(); + Expected<APValue> ToValueOrErr = import(From.getAsStructuralValue()); + if (!ToValueOrErr) + return ToValueOrErr.takeError(); + return TemplateArgument(Importer.getToContext(), *ToTypeOrErr, + *ToValueOrErr); + } + case TemplateArgument::Template: { Expected<TemplateName> ToTemplateOrErr = import(From.getAsTemplate()); if (!ToTemplateOrErr) @@ -3572,6 +3583,8 @@ class IsTypeDeclaredInsideVisitor case TemplateArgument::NullPtr: // FIXME: The type is not allowed to be in the function? return CheckType(Arg.getNullPtrType()); + case TemplateArgument::StructuralValue: + return CheckType(Arg.getStructuralValueType()); case TemplateArgument::Pack: for (const auto &PackArg : Arg.getPackAsArray()) if (checkTemplateArgument(PackArg)) diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index a9e0d1698a9178..918a9e02199a1f 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -628,6 +628,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return IsStructurallyEquivalent(Context, Arg1.getAsExpr(), Arg2.getAsExpr()); + case TemplateArgument::StructuralValue: + return Arg1.structurallyEquals(Arg2); + case TemplateArgument::Pack: return IsStructurallyEquivalent(Context, Arg1.pack_elements(), Arg2.pack_elements()); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index e1440e5183a4e6..a2f50ae4ce8eeb 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -343,6 +343,10 @@ LinkageComputer::getLVForTemplateArgumentList(ArrayRef<TemplateArgument> Args, LV.merge(getTypeLinkageAndVisibility(Arg.getNullPtrType())); continue; + case TemplateArgument::StructuralValue: + LV.merge(getLVForValue(Arg.getAsStructuralValue(), computation)); + continue; + case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: if (TemplateDecl *Template = diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index b1678479888eb7..60a3b6908a0a92 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4833,9 +4833,23 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, E = cast<CXXStdInitializerListExpr>(E)->getSubExpr(); goto recurse; - case Expr::SubstNonTypeTemplateParmExprClass: + case Expr::SubstNonTypeTemplateParmExprClass: { + // Mangle a substituted parameter the same way we mangle the template + // argument. + auto *SNTTPE = cast<SubstNonTypeTemplateParmExpr>(E); + if (auto *CE = dyn_cast<ConstantExpr>(SNTTPE->getReplacement())) { + // Pull out the constant value and mangle it as a template argument. + QualType ParamType = SNTTPE->getParameterType(Context.getASTContext()); + assert(CE->hasAPValueResult() && "expected the NTTP to have an APValue"); + mangleValueInTemplateArg(ParamType, CE->getAPValueResult(), false, + /*NeedExactType=*/true); + break; + } + // The remaining cases all happen to be substituted with expressions that + // mangle the same as a corresponding template argument anyway. E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); goto recurse; + } case Expr::UserDefinedLiteralClass: // We follow g++'s approach of mangling a UDL as a call to the literal @@ -6064,6 +6078,11 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { mangleNullPointer(A.getNullPtrType()); break; } + case TemplateArgument::StructuralValue: + mangleValueInTemplateArg(A.getStructuralValueType(), + A.getAsStructuralValue(), + /*TopLevel=*/true, NeedExactType); + break; case TemplateArgument::Pack: { // <template-arg> ::= J <template-arg>* E Out << 'J'; @@ -6472,7 +6491,20 @@ void CXXNameMangler::mangleValueInTemplateArg(QualType T, const APValue &V, Out << "plcvPcad"; Kind = Offset; } else { - if (!V.getLValuePath().empty() || V.isLValueOnePastTheEnd()) { + // Clang 11 and before mangled an array subject to array-to-pointer decay + // as if it were the declaration itself. + bool IsArrayToPointerDecayMangledAsDecl = false; + if (TopLevel && Ctx.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver11) { + QualType BType = B.getType(); + IsArrayToPointerDecayMangledAsDecl = + BType->isArrayType() && V.getLValuePath().size() == 1 && + V.getLValuePath()[0].getAsArrayIndex() == 0 && + Ctx.hasSimilarType(T, Ctx.getDecayedType(BType)); + } + + if ((!V.getLValuePath().empty() || V.isLValueOnePastTheEnd()) && + !IsArrayToPointerDecayMangledAsDecl) { NotPrimaryExpr(); // A final conversion to the template parameter's type is usually // folded into the 'so' mangling, but we can't do that for 'void*' diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 8346ad87b409b6..36b5bf64f675a8 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -337,6 +337,7 @@ class MicrosoftCXXNameMangler { public: enum QualifierMangleMode { QMM_Drop, QMM_Mangle, QMM_Escape, QMM_Result }; + enum class TplArgKind { ClassNTTP, StructuralValue }; MicrosoftCXXNameMangler(MicrosoftMangleContextImpl &C, raw_ostream &Out_) : Context(C), Out(Out_), Structor(nullptr), StructorType(-1), @@ -453,7 +454,7 @@ class MicrosoftCXXNameMangler { const TemplateArgumentList &TemplateArgs); void mangleTemplateArg(const TemplateDecl *TD, const TemplateArgument &TA, const NamedDecl *Parm); - void mangleTemplateArgValue(QualType T, const APValue &V, + void mangleTemplateArgValue(QualType T, const APValue &V, TplArgKind, bool WithScalarType = false); void mangleObjCProtocol(const ObjCProtocolDecl *PD); @@ -1088,7 +1089,7 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(GlobalDecl GD, if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) { Out << "?__N"; mangleTemplateArgValue(TPO->getType().getUnqualifiedType(), - TPO->getValue()); + TPO->getValue(), TplArgKind::ClassNTTP); break; } @@ -1604,6 +1605,22 @@ void MicrosoftCXXNameMangler::mangleTemplateArgs( } } +/// If value V (with type T) represents a decayed pointer to the first element +/// of an array, return that array. +static ValueDecl *getAsArrayToPointerDecayedDecl(QualType T, const APValue &V) { + // Must be a pointer... + if (!T->isPointerType() || !V.isLValue() || !V.hasLValuePath() || + !V.getLValueBase()) + return nullptr; + // ... to element 0 of an array. + QualType BaseT = V.getLValueBase().getType(); + if (!BaseT->isArrayType() || V.getLValuePath().size() != 1 || + V.getLValuePath()[0].getAsArrayIndex() != 0) + return nullptr; + return const_cast<ValueDecl *>( + V.getLValueBase().dyn_cast<const ValueDecl *>()); +} + void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, const TemplateArgument &TA, const NamedDecl *Parm) { @@ -1669,7 +1686,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, Out << "$"; auto *TPO = cast<TemplateParamObjectDecl>(ND); mangleTemplateArgValue(TPO->getType().getUnqualifiedType(), - TPO->getValue()); + TPO->getValue(), TplArgKind::ClassNTTP); } else { mangle(ND, "$1?"); } @@ -1712,6 +1729,27 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, cast<NonTypeTemplateParmDecl>(Parm), T); break; } + case TemplateArgument::StructuralValue: + if (ValueDecl *D = getAsArrayToPointerDecayedDecl( + TA.getStructuralValueType(), TA.getAsStructuralValue())) { + // Mangle the result of array-to-pointer decay as if it were a reference + // to the original declaration, to match MSVC's behavior. This can result + // in mangling collisions in some cases! + return mangleTemplateArg( + TD, TemplateArgument(D, TA.getStructuralValueType()), Parm); + } + Out << "$"; + if (cast<NonTypeTemplateParmDecl>(Parm) + ->getType() + ->getContainedDeducedType()) { + Out << "M"; + mangleType(TA.getNonTypeTemplateArgumentType(), SourceRange(), QMM_Drop); + } + mangleTemplateArgValue(TA.getStructuralValueType(), + TA.getAsStructuralValue(), + TplArgKind::StructuralValue, + /*WithScalarType=*/false); + break; case TemplateArgument::Expression: mangleExpression(TA.getAsExpr(), cast<NonTypeTemplateParmDecl>(Parm)); break; @@ -1754,6 +1792,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, const APValue &V, + TplArgKind TAK, bool WithScalarType) { switch (V.getKind()) { case APValue::None: @@ -1806,7 +1845,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, break; } } else { - if (T->isPointerType()) + if (TAK == TplArgKind::ClassNTTP && T->isPointerType()) Out << "5"; SmallVector<char, 2> EntryTypes; @@ -1850,12 +1889,12 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, auto *VD = Base.dyn_cast<const ValueDecl*>(); if (!VD) break; - Out << "E"; + Out << (TAK == TplArgKind::ClassNTTP ? 'E' : '1'); mangle(VD); for (const std::function<void()> &Mangler : EntryManglers) Mangler(); - if (T->isPointerType()) + if (TAK == TplArgKind::ClassNTTP && T->isPointerType()) Out << '@'; } @@ -1869,11 +1908,18 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, const CXXRecordDecl *RD = T->castAs<MemberPointerType>()->getMostRecentCXXRecordDecl(); const ValueDecl *D = V.getMemberPointerDecl(); - if (T->isMemberDataPointerType()) - mangleMemberDataPointerInClassNTTP(RD, D); - else - mangleMemberFunctionPointerInClassNTTP(RD, - cast_or_null<CXXMethodDecl>(D)); + if (TAK == TplArgKind::ClassNTTP) { + if (T->isMemberDataPointerType()) + mangleMemberDataPointerInClassNTTP(RD, D); + else + mangleMemberFunctionPointerInClassNTTP(RD, + cast_or_null<CXXMethodDecl>(D)); + } else { + if (T->isMemberDataPointerType()) + mangleMemberDataPointer(RD, D, ""); + else + mangleMemberFunctionPointer(RD, cast_or_null<CXXMethodDecl>(D), ""); + } return; } @@ -1885,11 +1931,11 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, unsigned BaseIndex = 0; for (const CXXBaseSpecifier &B : RD->bases()) - mangleTemplateArgValue(B.getType(), V.getStructBase(BaseIndex++)); + mangleTemplateArgValue(B.getType(), V.getStructBase(BaseIndex++), TAK); for (const FieldDecl *FD : RD->fields()) if (!FD->isUnnamedBitfield()) mangleTemplateArgValue(FD->getType(), - V.getStructField(FD->getFieldIndex()), + V.getStructField(FD->getFieldIndex()), TAK, /*WithScalarType*/ true); Out << '@'; return; @@ -1900,7 +1946,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, mangleType(T, SourceRange(), QMM_Escape); if (const FieldDecl *FD = V.getUnionField()) { mangleUnqualifiedName(FD); - mangleTemplateArgValue(FD->getType(), V.getUnionValue()); + mangleTemplateArgValue(FD->getType(), V.getUnionValue(), TAK); } Out << '@'; return; @@ -1932,7 +1978,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, const APValue &ElemV = I < V.getArrayInitializedElts() ? V.getArrayInitializedElt(I) : V.getArrayFiller(); - mangleTemplateArgValue(ElemT, ElemV); + mangleTemplateArgValue(ElemT, ElemV, TAK); Out << '@'; } Out << '@'; @@ -1949,7 +1995,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, mangleType(ElemT, SourceRange(), QMM_Escape); for (unsigned I = 0, N = V.getVectorLength(); I != N; ++I) { const APValue &ElemV = V.getVectorElt(I); - mangleTemplateArgValue(ElemT, ElemV); + mangleTemplateArgValue(ElemT, ElemV, TAK); Out << '@'; } Out << "@@"; diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index aea1a93ae1fa82..85f5a1be96ecf9 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -180,6 +180,10 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) { TA.getAsIntegral().Profile(ID); break; } + case TemplateArgument::StructuralValue: + AddQualType(TA.getStructuralValueType()); + AddStructuralValue(TA.getAsStructuralValue()); + break; case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: AddTemplateName(TA.getAsTemplateOrTemplatePattern()); @@ -1249,3 +1253,66 @@ void ODRHash::AddQualType(QualType T) { void ODRHash::AddBoolean(bool Value) { Bools.push_back(Value); } + +void ODRHash::AddStructuralValue(const APValue &Value) { + ID.AddInteger(Value.getKind()); + + // 'APValue::Profile' uses pointer values to make hash for LValue and + // MemberPointer, but they differ from one compiler invocation to another. + // So, handle them explicitly here. + + switch (Value.getKind()) { + case APValue::LValue: { + const APValue::LValueBase &Base = Value.getLValueBase(); + if (!Base) { + ID.AddInteger(Value.getLValueOffset().getQuantity()); + break; + } + + assert(Base.is<const ValueDecl *>()); + AddDecl(Base.get<const ValueDecl *>()); + ID.AddInteger(Value.getLValueOffset().getQuantity()); + + bool OnePastTheEnd = Value.isLValueOnePastTheEnd(); + if (Value.hasLValuePath()) { + QualType TypeSoFar = Base.getType(); + for (APValue::LValuePathEntry E : Value.getLValuePath()) { + if (const auto *AT = TypeSoFar->getAsArrayTypeUnsafe()) { + if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) + OnePastTheEnd |= CAT->getSize() == E.getAsArrayIndex(); + TypeSoFar = AT->getElementType(); + } else { + const Decl *D = E.getAsBaseOrMember().getPointer(); + if (const auto *FD = dyn_cast<FieldDecl>(D)) { + if (FD->getParent()->isUnion()) + ID.AddInteger(FD->getFieldIndex()); + TypeSoFar = FD->getType(); + } else { + TypeSoFar = + D->getASTContext().getRecordType(cast<CXXRecordDecl>(D)); + } + } + } + } + unsigned Val = 0; + if (Value.isNullPointer()) + Val |= 1 << 0; + if (OnePastTheEnd) + Val |= 1 << 1; + if (Value.hasLValuePath()) + Val |= 1 << 2; + ID.AddInteger(Val); + break; + } + case APValue::MemberPointer: { + const ValueDecl *D = Value.getMemberPointerDecl(); + assert(D); + AddDecl(D); + ID.AddInteger( + D->getASTContext().getMemberPointerPathAdjustment(Value).getQuantity()); + break; + } + default: + Value.Profile(ID); + } +} diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index d7b980a585702a..dd0838edab7b3f 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2416,6 +2416,12 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { Arg.getAsIntegral().Profile(ID); break; + case TemplateArgument::StructuralValue: + VisitType(Arg.getStructuralValueType()); + // FIXME: Do we need to recursively decompose this ourselves? + Arg.getAsStructuralValue().Profile(ID); + break; + case TemplateArgument::Expression: Visit(Arg.getAsExpr()); break; diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index 2ec0b2cce08ca4..2bdbeb08ef2046 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -161,8 +161,25 @@ static bool needsAmpersandOnTemplateArg(QualType paramType, QualType argType) { // TemplateArgument Implementation //===----------------------------------------------------------------------===// -TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, - QualType Type, bool IsDefaulted) { +void TemplateArgument::initFromType(QualType T, bool IsNullPtr, + bool IsDefaulted) { + TypeOrValue.Kind = IsNullPtr ? NullPtr : Type; + TypeOrValue.IsDefaulted = IsDefaulted; + TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr()); +} + +void TemplateArgument::initFromDeclaration(ValueDecl *D, QualType QT, + bool IsDefaulted) { + assert(D && "Expected decl"); + DeclArg.Kind = Declaration; + DeclArg.IsDefaulted = IsDefaulted; + DeclArg.QT = QT.getAsOpaquePtr(); + DeclArg.D = D; +} + +void TemplateArgument::initFromIntegral(const ASTContext &Ctx, + const llvm::APSInt &Value, + QualType Type, bool IsDefaulted) { Integer.Kind = Integral; Integer.IsDefaulted = IsDefaulted; // Copy the APSInt value into our decomposed form. @@ -181,6 +198,56 @@ TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, Integer.Type = Type.getAsOpaquePtr(); } +void TemplateArgument::initFromStructural(const ASTContext &Ctx, QualType Type, + const APValue &V, bool IsDefaulted) { + Value.Kind = StructuralValue; + Value.IsDefaulted = IsDefaulted; + Value.Value = new (Ctx) APValue(V); + Ctx.addDestruction(Value.Value); + Value.Type = Type.getAsOpaquePtr(); +} + +TemplateArgument::TemplateArgument(const ASTContext &Ctx, + const llvm::APSInt &Value, QualType Type, + bool IsDefaulted) { + initFromIntegral(Ctx, Value, Type, IsDefaulted); +} + +static const ValueDecl *getAsSimpleValueDeclRef(const ASTContext &Ctx, + QualType T, const APValue &V) { + // Pointers to members are relatively easy. + if (V.isMemberPointer() && V.getMemberPointerPath().empty()) + return V.getMemberPointerDecl(); + + // We model class non-type template parameters as their template parameter + // object declaration. + if (V.isStruct() || V.isUnion()) + return Ctx.getTemplateParamObjectDecl(T, V); + + // Pointers and references with an empty path use the special 'Declaration' + // representation. + if (V.isLValue() && V.hasLValuePath() && V.getLValuePath().empty() && + !V.isLValueOnePastTheEnd()) + return V.getLValueBase().dyn_cast<const ValueDecl *>(); + + // Everything else uses the 'structural' representation. + return nullptr; +} + +TemplateArgument::TemplateArgument(const ASTContext &Ctx, QualType Type, + const APValue &V, bool IsDefaulted) { + if (Type->isIntegralOrEnumerationType() && V.isInt()) + initFromIntegral(Ctx, V.getInt(), Type, IsDefaulted); + else if ((V.isLValue() && V.isNullPointer()) || + (V.isMemberPointer() && !V.getMemberPointerDecl())) + initFromType(Type, /*isNullPtr=*/true, IsDefaulted); + else if (const ValueDecl *VD = getAsSimpleValueDeclRef(Ctx, Type, V)) + // FIXME: The Declaration form should expose a const ValueDecl*. + initFromDeclaration(const_cast<ValueDecl *>(VD), Type, IsDefaulted); + else + initFromStructural(Ctx, Type, V, IsDefaulted); +} + TemplateArgument TemplateArgument::CreatePackCopy(ASTContext &Context, ArrayRef<TemplateArgument> Args) { @@ -221,6 +288,7 @@ TemplateArgumentDependence TemplateArgument::getDependence() const { case NullPtr: case Integral: + case StructuralValue: return TemplateArgumentDependence::None; case Expression: @@ -251,6 +319,7 @@ bool TemplateArgument::isPackExpansion() const { case Null: case Declaration: case Integral: + case StructuralValue: case Pack: case Template: case NullPtr: @@ -301,6 +370,9 @@ QualType TemplateArgument::getNonTypeTemplateArgumentType() const { case TemplateArgument::NullPtr: return getNullPtrType(); + + case TemplateArgument::StructuralValue: + return getStructuralValueType(); } llvm_unreachable("Invalid TemplateArgument Kind!"); @@ -334,8 +406,13 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, break; case Integral: - getAsIntegral().Profile(ID); getIntegralType().Profile(ID); + getAsIntegral().Profile(ID); + break; + + case StructuralValue: + getStructuralValueType().Profile(ID); + getAsStructuralValue().Profile(ID); break; case Expression: @@ -372,6 +449,16 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { return getIntegralType() == Other.getIntegralType() && getAsIntegral() == Other.getAsIntegral(); + case StructuralValue: { + if (getStructuralValueType() != Other.getStructuralValueType()) + return false; + + llvm::FoldingSetNodeID A, B; + getAsStructuralValue().Profile(A); + Other.getAsStructuralValue().Profile(B); + return A == B; + } + case Pack: if (Args.NumArgs != Other.Args.NumArgs) return false; for (unsigned I = 0, E = Args.NumArgs; I != E; ++I) @@ -398,6 +485,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const { case Declaration: case Integral: + case StructuralValue: case Pack: case Null: case Template: @@ -440,6 +528,10 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out, break; } + case StructuralValue: + getAsStructuralValue().printPretty(Out, Policy, getStructuralValueType()); + break; + case NullPtr: // FIXME: Include the type if it's not obvious from the context. Out << "nullptr"; @@ -523,6 +615,9 @@ SourceRange TemplateArgumentLoc::getSourceRange() const { case TemplateArgument::Integral: return getSourceIntegralExpression()->getSourceRange(); + case TemplateArgument::StructuralValue: + return getSourceStructuralValueExpression()->getSourceRange(); + case TemplateArgument::Pack: case TemplateArgument::Null: return SourceRange(); @@ -551,6 +646,18 @@ static const T &DiagTemplateArg(const T &DB, const TemplateArgument &Arg) { case TemplateArgument::Integral: return DB << toString(Arg.getAsIntegral(), 10); + case TemplateArgument::StructuralValue: { + // FIXME: We're guessing at LangOptions! + SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + LangOptions LangOpts; + LangOpts.CPlusPlus = true; + PrintingPolicy Policy(LangOpts); + Arg.getAsStructuralValue().printPretty(OS, Policy, + Arg.getStructuralValueType()); + return DB << OS.str(); + } + case TemplateArgument::Template: return DB << Arg.getAsTemplate(); diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index e12b9b50f6e722..66732bba18e2d6 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -586,6 +586,7 @@ void TemplateSpecializationTypeLoc::initializeArgLocs( case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: ArgInfos[i] = TemplateArgumentLocInfo(); break; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 236d53bee4e8f1..88ec7baf1156df 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2201,6 +2201,14 @@ CGDebugInfo::CollectTemplateParams(std::optional<TemplateArgs> OArgs, TemplateParams.push_back(DBuilder.createTemplateValueParameter( TheCU, Name, TTy, defaultParameter, V)); } break; + case TemplateArgument::StructuralValue: { + QualType T = TA.getStructuralValueType(); + llvm::DIType *TTy = getOrCreateType(T, Unit); + llvm::Constant *V = ConstantEmitter(CGM).emitAbstract( + SourceLocation(), TA.getAsStructuralValue(), T); + TemplateParams.push_back(DBuilder.createTemplateValueParameter( + TheCU, Name, TTy, defaultParameter, V)); + } break; case TemplateArgument::Template: { std::string QualName; llvm::raw_string_ostream OS(QualName); @@ -5401,6 +5409,8 @@ std::string CGDebugInfo::GetName(const Decl *D, bool Qualified) const { // feasible some day. return TA.getAsIntegral().getBitWidth() <= 64 && IsReconstitutableType(TA.getIntegralType()); + case TemplateArgument::StructuralValue: + return false; case TemplateArgument::Type: return IsReconstitutableType(TA.getAsType()); default: diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index d12e85b48d0b00..c5f6b6d3a99f0b 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1516,6 +1516,14 @@ LValue CodeGenFunction::EmitLValue(const Expr *E, return LV; } +static QualType getConstantExprReferredType(const FullExpr *E, + const ASTContext &Ctx) { + const Expr *SE = E->getSubExpr()->IgnoreImplicit(); + if (isa<OpaqueValueExpr>(SE)) + return SE->getType(); + return cast<CallExpr>(SE)->getCallReturnType(Ctx)->getPointeeType(); +} + LValue CodeGenFunction::EmitLValueHelper(const Expr *E, KnownNonNull_t IsKnownNonNull) { ApplyDebugLocation DL(*this, E); @@ -1554,9 +1562,7 @@ LValue CodeGenFunction::EmitLValueHelper(const Expr *E, case Expr::ConstantExprClass: { const ConstantExpr *CE = cast<ConstantExpr>(E); if (llvm::Value *Result = ConstantEmitter(*this).tryEmitConstantExpr(CE)) { - QualType RetType = cast<CallExpr>(CE->getSubExpr()->IgnoreImplicit()) - ->getCallReturnType(getContext()) - ->getPointeeType(); + QualType RetType = getConstantExprReferredType(CE, getContext()); return MakeNaturalAlignAddrLValue(Result, RetType); } return EmitLValue(cast<ConstantExpr>(E)->getSubExpr(), IsKnownNonNull); diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp index fb936d9fbf8ab4..5acc86191f8f9c 100644 --- a/clang/lib/Index/USRGeneration.cpp +++ b/clang/lib/Index/USRGeneration.cpp @@ -12,6 +12,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" +#include "clang/AST/ODRHash.h" #include "clang/Basic/FileManager.h" #include "clang/Lex/PreprocessingRecord.h" #include "llvm/Support/Path.h" @@ -1051,6 +1052,15 @@ void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) { VisitType(Arg.getIntegralType()); Out << Arg.getAsIntegral(); break; + + case TemplateArgument::StructuralValue: { + Out << 'S'; + VisitType(Arg.getStructuralValueType()); + ODRHash Hash{}; + Hash.AddStructuralValue(Arg.getAsStructuralValue()); + Out << Hash.CalculateHash(); + break; + } } } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 996f8b57233ba2..02b1a045df44c2 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2982,6 +2982,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, case TemplateArgument::Integral: case TemplateArgument::Expression: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: // [Note: non-type template arguments do not contribute to the set of // associated namespaces. ] break; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 23b9bc0fe2d6e2..de57bed4e5f371 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6213,7 +6213,15 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, if (Notes.empty()) { // It's a constant expression. - Expr *E = ConstantExpr::Create(Context, Result.get(), Value); + Expr *E = Result.get(); + if (const auto *CE = dyn_cast<ConstantExpr>(E)) { + // We expect a ConstantExpr to have a value associated with it + // by this point. + assert(CE->getResultStorageKind() != ConstantResultStorageKind::None && + "ConstantExpr has no value associated with it"); + } else { + E = ConstantExpr::Create(Context, Result.get(), Value); + } if (!PreNarrowingValue.isAbsent()) Value = std::move(PreNarrowingValue); return E; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 5fcc39ec700522..226b48268c8c77 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4414,6 +4414,7 @@ static bool isTemplateArgumentTemplateParameter( case TemplateArgument::NullPtr: case TemplateArgument::Integral: case TemplateArgument::Declaration: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: return false; @@ -5745,6 +5746,7 @@ bool Sema::CheckTemplateArgument( case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: case TemplateArgument::NullPtr: // We've already checked this template argument, so just copy // it to the list of converted arguments. @@ -5899,11 +5901,10 @@ bool Sema::CheckTemplateArgument( return true; case TemplateArgument::Declaration: - llvm_unreachable("Declaration argument with template template parameter"); case TemplateArgument::Integral: - llvm_unreachable("Integral argument with template template parameter"); + case TemplateArgument::StructuralValue: case TemplateArgument::NullPtr: - llvm_unreachable("Null pointer argument with template template parameter"); + llvm_unreachable("non-type argument with template template parameter"); case TemplateArgument::Pack: llvm_unreachable("Caller must expand template argument packs"); @@ -7398,44 +7399,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, if (ArgResult.isInvalid()) return ExprError(); - // Convert the APValue to a TemplateArgument. - switch (Value.getKind()) { - case APValue::None: - assert(ParamType->isNullPtrType()); - SugaredConverted = TemplateArgument(ParamType, /*isNullPtr=*/true); - CanonicalConverted = TemplateArgument(CanonParamType, /*isNullPtr=*/true); - break; - case APValue::Indeterminate: - llvm_unreachable("result of constant evaluation should be initialized"); - break; - case APValue::Int: - assert(ParamType->isIntegralOrEnumerationType()); - SugaredConverted = TemplateArgument(Context, Value.getInt(), ParamType); - CanonicalConverted = - TemplateArgument(Context, Value.getInt(), CanonParamType); - break; - case APValue::MemberPointer: { - assert(ParamType->isMemberPointerType()); - - // FIXME: We need TemplateArgument representation and mangling for these. - if (!Value.getMemberPointerPath().empty()) { - Diag(Arg->getBeginLoc(), - diag::err_template_arg_member_ptr_base_derived_not_supported) - << Value.getMemberPointerDecl() << ParamType - << Arg->getSourceRange(); - return ExprError(); - } - - auto *VD = const_cast<ValueDecl*>(Value.getMemberPointerDecl()); - SugaredConverted = VD ? TemplateArgument(VD, ParamType) - : TemplateArgument(ParamType, /*isNullPtr=*/true); - CanonicalConverted = - VD ? TemplateArgument(cast<ValueDecl>(VD->getCanonicalDecl()), - CanonParamType) - : TemplateArgument(CanonParamType, /*isNullPtr=*/true); - break; - } - case APValue::LValue: { + // Prior to C++20, enforce restrictions on possible template argument + // values. + if (!getLangOpts().CPlusPlus20 && Value.isLValue()) { // For a non-type template-parameter of pointer or reference type, // the value of the constant expression shall not refer to assert(ParamType->isPointerType() || ParamType->isReferenceType() || @@ -7453,8 +7419,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, << Arg->getSourceRange(); return ExprError(); } - // -- a subobject - // FIXME: Until C++20 + // -- a subobject [until C++20] if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 && VD && VD->getType()->isArrayType() && Value.getLValuePath()[0].getAsArrayIndex() == 0 && @@ -7472,37 +7437,13 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, "null reference should not be a constant expression"); assert((!VD || !ParamType->isNullPtrType()) && "non-null value of type nullptr_t?"); - - SugaredConverted = VD ? TemplateArgument(VD, ParamType) - : TemplateArgument(ParamType, /*isNullPtr=*/true); - CanonicalConverted = - VD ? TemplateArgument(cast<ValueDecl>(VD->getCanonicalDecl()), - CanonParamType) - : TemplateArgument(CanonParamType, /*isNullPtr=*/true); - break; - } - case APValue::Struct: - case APValue::Union: { - // Get or create the corresponding template parameter object. - TemplateParamObjectDecl *D = - Context.getTemplateParamObjectDecl(ParamType, Value); - SugaredConverted = TemplateArgument(D, ParamType); - CanonicalConverted = - TemplateArgument(D->getCanonicalDecl(), CanonParamType); - break; } - case APValue::AddrLabelDiff: + + if (Value.isAddrLabelDiff()) return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff); - case APValue::FixedPoint: - case APValue::Float: - case APValue::ComplexInt: - case APValue::ComplexFloat: - case APValue::Vector: - case APValue::Array: - return Diag(StartLoc, diag::err_non_type_template_arg_unsupported) - << ParamType; - } + SugaredConverted = TemplateArgument(Context, ParamType, Value); + CanonicalConverted = TemplateArgument(Context, CanonParamType, Value); return ArgResult.get(); } @@ -8086,12 +8027,9 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, /// This routine takes care of the mapping from an integral template /// argument (which may have any integral type) to the appropriate /// literal value. -ExprResult -Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, - SourceLocation Loc) { - assert(Arg.getKind() == TemplateArgument::Integral && - "Operation is only valid for integral template arguments"); - QualType OrigT = Arg.getIntegralType(); +static Expr *BuildExpressionFromIntegralTemplateArgumentValue( + Sema &S, QualType OrigT, const llvm::APSInt &Int, SourceLocation Loc) { + assert(OrigT->isIntegralOrEnumerationType()); // If this is an enum type that we're instantiating, we need to use an integer // type the same size as the enumerator. We don't want to build an @@ -8107,7 +8045,7 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, CharacterLiteralKind Kind; if (T->isWideCharType()) Kind = CharacterLiteralKind::Wide; - else if (T->isChar8Type() && getLangOpts().Char8) + else if (T->isChar8Type() && S.getLangOpts().Char8) Kind = CharacterLiteralKind::UTF8; else if (T->isChar16Type()) Kind = CharacterLiteralKind::UTF16; @@ -8116,29 +8054,133 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, else Kind = CharacterLiteralKind::Ascii; - E = new (Context) CharacterLiteral(Arg.getAsIntegral().getZExtValue(), - Kind, T, Loc); + E = new (S.Context) CharacterLiteral(Int.getZExtValue(), Kind, T, Loc); } else if (T->isBooleanType()) { - E = CXXBoolLiteralExpr::Create(Context, Arg.getAsIntegral().getBoolValue(), - T, Loc); - } else if (T->isNullPtrType()) { - E = new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc); + E = CXXBoolLiteralExpr::Create(S.Context, Int.getBoolValue(), T, Loc); } else { - E = IntegerLiteral::Create(Context, Arg.getAsIntegral(), T, Loc); + E = IntegerLiteral::Create(S.Context, Int, T, Loc); } if (OrigT->isEnumeralType()) { // FIXME: This is a hack. We need a better way to handle substituted // non-type template parameters. - E = CStyleCastExpr::Create(Context, OrigT, VK_PRValue, CK_IntegralCast, E, - nullptr, CurFPFeatureOverrides(), - Context.getTrivialTypeSourceInfo(OrigT, Loc), + E = CStyleCastExpr::Create(S.Context, OrigT, VK_PRValue, CK_IntegralCast, E, + nullptr, S.CurFPFeatureOverrides(), + S.Context.getTrivialTypeSourceInfo(OrigT, Loc), Loc, Loc); } return E; } +static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( + Sema &S, QualType T, const APValue &Val, SourceLocation Loc) { + auto MakeInitList = [&](ArrayRef<Expr *> Elts) -> Expr * { + auto *ILE = new (S.Context) InitListExpr(S.Context, Loc, Elts, Loc); + ILE->setType(T); + return ILE; + }; + + switch (Val.getKind()) { + case APValue::AddrLabelDiff: + // This cannot occur in a template argument at all. + case APValue::Array: + case APValue::Struct: + case APValue::Union: + // These can only occur within a template parameter object, which is + // represented as a TemplateArgument::Declaration. + llvm_unreachable("unexpected template argument value"); + + case APValue::Int: + return BuildExpressionFromIntegralTemplateArgumentValue(S, T, Val.getInt(), + Loc); + + case APValue::Float: + return FloatingLiteral::Create(S.Context, Val.getFloat(), /*IsExact=*/true, + T, Loc); + + case APValue::FixedPoint: + return FixedPointLiteral::CreateFromRawInt( + S.Context, Val.getFixedPoint().getValue(), T, Loc, + Val.getFixedPoint().getScale()); + + case APValue::ComplexInt: { + QualType ElemT = T->castAs<ComplexType>()->getElementType(); + return MakeInitList({BuildExpressionFromIntegralTemplateArgumentValue( + S, ElemT, Val.getComplexIntReal(), Loc), + BuildExpressionFromIntegralTemplateArgumentValue( + S, ElemT, Val.getComplexIntImag(), Loc)}); + } + + case APValue::ComplexFloat: { + QualType ElemT = T->castAs<ComplexType>()->getElementType(); + return MakeInitList( + {FloatingLiteral::Create(S.Context, Val.getComplexFloatReal(), true, + ElemT, Loc), + FloatingLiteral::Create(S.Context, Val.getComplexFloatImag(), true, + ElemT, Loc)}); + } + + case APValue::Vector: { + QualType ElemT = T->castAs<VectorType>()->getElementType(); + llvm::SmallVector<Expr *, 8> Elts; + for (unsigned I = 0, N = Val.getVectorLength(); I != N; ++I) + Elts.push_back(BuildExpressionFromNonTypeTemplateArgumentValue( + S, ElemT, Val.getVectorElt(I), Loc)); + return MakeInitList(Elts); + } + + case APValue::None: + case APValue::Indeterminate: + llvm_unreachable("Unexpected APValue kind."); + case APValue::LValue: + case APValue::MemberPointer: + // There isn't necessarily a valid equivalent source-level syntax for + // these; in particular, a naive lowering might violate access control. + // So for now we lower to a ConstantExpr holding the value, wrapped around + // an OpaqueValueExpr. + // FIXME: We should have a better representation for this. + ExprValueKind VK = VK_PRValue; + if (T->isReferenceType()) { + T = T->getPointeeType(); + VK = VK_LValue; + } + auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK); + return ConstantExpr::Create(S.Context, OVE, Val); + } + llvm_unreachable("Unhandled APValue::ValueKind enum"); +} + +ExprResult +Sema::BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, + SourceLocation Loc) { + switch (Arg.getKind()) { + case TemplateArgument::Null: + case TemplateArgument::Type: + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + case TemplateArgument::Pack: + llvm_unreachable("not a non-type template argument"); + + case TemplateArgument::Expression: + return Arg.getAsExpr(); + + case TemplateArgument::NullPtr: + case TemplateArgument::Declaration: + return BuildExpressionFromDeclTemplateArgument( + Arg, Arg.getNonTypeTemplateArgumentType(), Loc); + + case TemplateArgument::Integral: + return BuildExpressionFromIntegralTemplateArgumentValue( + *this, Arg.getIntegralType(), Arg.getAsIntegral(), Loc); + + case TemplateArgument::StructuralValue: + return BuildExpressionFromNonTypeTemplateArgumentValue( + *this, Arg.getStructuralValueType(), Arg.getAsStructuralValue(), Loc); + } + llvm_unreachable("Unhandled TemplateArgument::ArgKind enum"); +} + /// Match two template parameters within template parameter lists. static bool MatchTemplateParameterKind( Sema &S, NamedDecl *New, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 015b0abaf0e5ee..e9e7ab5bb6698a 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -268,6 +268,16 @@ checkDeducedTemplateArguments(ASTContext &Context, // All other combinations are incompatible. return DeducedTemplateArgument(); + case TemplateArgument::StructuralValue: + // If we deduced a value and a dependent expression, keep the value. + if (Y.getKind() == TemplateArgument::Expression || + (Y.getKind() == TemplateArgument::StructuralValue && + X.structurallyEquals(Y))) + return X; + + // All other combinations are incompatible. + return DeducedTemplateArgument(); + case TemplateArgument::Template: if (Y.getKind() == TemplateArgument::Template && Context.hasSameTemplateName(X.getAsTemplate(), Y.getAsTemplate())) @@ -2300,27 +2310,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, Info.SecondArg = A; return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::StructuralValue: + if (A.getKind() == TemplateArgument::StructuralValue && + A.structurallyEquals(P)) + return Sema::TDK_Success; + + Info.FirstArg = P; + Info.SecondArg = A; + return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::Expression: if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { - if (A.getKind() == TemplateArgument::Integral) + switch (A.getKind()) { + case TemplateArgument::Integral: + case TemplateArgument::Expression: + case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( - S, TemplateParams, NTTP, A.getAsIntegral(), A.getIntegralType(), - /*ArrayBound=*/false, Info, Deduced); - if (A.getKind() == TemplateArgument::NullPtr) + S, TemplateParams, NTTP, DeducedTemplateArgument(A), + A.getNonTypeTemplateArgumentType(), Info, Deduced); + + case TemplateArgument::NullPtr: return DeduceNullPtrTemplateArgument(S, TemplateParams, NTTP, A.getNullPtrType(), Info, Deduced); - if (A.getKind() == TemplateArgument::Expression) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - A.getAsExpr(), Info, Deduced); - if (A.getKind() == TemplateArgument::Declaration) + + case TemplateArgument::Declaration: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, A.getAsDecl(), A.getParamTypeForDecl(), Info, Deduced); - Info.FirstArg = P; - Info.SecondArg = A; - return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::Null: + case TemplateArgument::Type: + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + case TemplateArgument::Pack: + Info.FirstArg = P; + Info.SecondArg = A; + return Sema::TDK_NonDeducedMismatch; + } + llvm_unreachable("Unknown template argument kind"); } // Can't deduce anything, but that's okay. @@ -2505,6 +2533,9 @@ static bool isSameTemplateArg(ASTContext &Context, case TemplateArgument::Integral: return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()); + case TemplateArgument::StructuralValue: + return X.structurallyEquals(Y); + case TemplateArgument::Expression: { llvm::FoldingSetNodeID XID, YID; X.getAsExpr()->Profile(XID, Context, true); @@ -2585,9 +2616,9 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, E); } - case TemplateArgument::Integral: { - Expr *E = - BuildExpressionFromIntegralTemplateArgument(Arg, Loc).getAs<Expr>(); + case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: { + Expr *E = BuildExpressionFromNonTypeTemplateArgument(Arg, Loc).get(); return TemplateArgumentLoc(TemplateArgument(E), E); } @@ -6449,11 +6480,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx, case TemplateArgument::Null: case TemplateArgument::Integral: case TemplateArgument::Declaration: - break; - case TemplateArgument::NullPtr: - MarkUsedTemplateParameters(Ctx, TemplateArg.getNullPtrType(), OnlyDeduced, - Depth, Used); + case TemplateArgument::StructuralValue: break; case TemplateArgument::Type: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index fc80515b45e35b..e12186d7d82f8d 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1975,9 +1975,8 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( } } else if (arg.getKind() == TemplateArgument::Declaration || arg.getKind() == TemplateArgument::NullPtr) { - ValueDecl *VD; if (arg.getKind() == TemplateArgument::Declaration) { - VD = arg.getAsDecl(); + ValueDecl *VD = arg.getAsDecl(); // Find the instantiation of the template argument. This is // required for nested templates. @@ -1985,21 +1984,20 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( getSema().FindInstantiatedDecl(loc, VD, TemplateArgs)); if (!VD) return ExprError(); - } else { - // Propagate NULL template argument. - VD = nullptr; } - QualType paramType = VD ? arg.getParamTypeForDecl() : arg.getNullPtrType(); + QualType paramType = arg.getNonTypeTemplateArgumentType(); assert(!paramType.isNull() && "type substitution failed for param type"); assert(!paramType->isDependentType() && "param type still dependent"); result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, paramType, loc); refParam = paramType->isReferenceType(); } else { - result = SemaRef.BuildExpressionFromIntegralTemplateArgument(arg, loc); + QualType paramType = arg.getNonTypeTemplateArgumentType(); + result = SemaRef.BuildExpressionFromNonTypeTemplateArgument(arg, loc); + refParam = paramType->isReferenceType(); assert(result.isInvalid() || SemaRef.Context.hasSameType(result.get()->getType(), - arg.getIntegralType())); + paramType.getNonReferenceType())); } if (result.isInvalid()) diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 0f4e9e7f94c81e..4a7872b2cc73c5 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -1112,6 +1112,7 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern( case TemplateArgument::NullPtr: case TemplateArgument::Template: case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::Null: return TemplateArgumentLoc(); @@ -1162,6 +1163,7 @@ std::optional<unsigned> Sema::getFullyPackExpandedSize(TemplateArgument Arg) { case TemplateArgument::NullPtr: case TemplateArgument::TemplateExpansion: case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::Null: return std::nullopt; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1a1bc87d2b3203..89fd4ea596dbac 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3908,6 +3908,7 @@ class TreeTransform { case TemplateArgument::Null: case TemplateArgument::Integral: case TemplateArgument::Declaration: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: case TemplateArgument::NullPtr: @@ -4577,7 +4578,8 @@ bool TreeTransform<Derived>::TransformTemplateArgument( case TemplateArgument::Integral: case TemplateArgument::NullPtr: - case TemplateArgument::Declaration: { + case TemplateArgument::Declaration: + case TemplateArgument::StructuralValue: { // Transform a resolved template argument straight to a resolved template // argument. We get here when substituting into an already-substituted // template type argument during concept satisfaction checking. @@ -4604,9 +4606,15 @@ bool TreeTransform<Derived>::TransformTemplateArgument( else if (Arg.getKind() == TemplateArgument::NullPtr) Output = TemplateArgumentLoc(TemplateArgument(NewT, /*IsNullPtr=*/true), TemplateArgumentLocInfo()); - else + else if (Arg.getKind() == TemplateArgument::Declaration) Output = TemplateArgumentLoc(TemplateArgument(NewD, NewT), TemplateArgumentLocInfo()); + else if (Arg.getKind() == TemplateArgument::StructuralValue) + Output = TemplateArgumentLoc( + TemplateArgument(getSema().Context, NewT, Arg.getAsStructuralValue()), + TemplateArgumentLocInfo()); + else + llvm_unreachable("unexpected template argument kind"); return false; } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 287f9a0300be5c..fe8782a3eb9e7c 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7460,6 +7460,7 @@ ASTRecordReader::readTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind) { case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: // FIXME: Is this right? return TemplateArgumentLocInfo(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 9950fa9c08faaa..26a235f3874d6e 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5608,6 +5608,7 @@ void ASTRecordWriter::AddTemplateArgumentLocInfo( case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: // FIXME: Is this right? break; diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp index 1d76804907a5c1..4c614a27bffd81 100644 --- a/clang/test/CXX/drs/dr18xx.cpp +++ b/clang/test/CXX/drs/dr18xx.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98-14,cxx98 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,cxx98-14,cxx11-17,since-cxx11 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,cxx98-14,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,cxx11-17,since-cxx11,since-cxx14,cxx17 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors @@ -25,7 +25,7 @@ template <int &> struct S {}; // #dr1801-S S<i> V; // #dr1801-S-i // cxx98-14-error@-1 {{non-type template argument does not refer to any declaration}} // cxx98-14-note@#dr1801-S {{template parameter is declared here}} -// since-cxx17-error@#dr1801-S-i {{non-type template argument refers to subobject '.i'}} +// cxx17-error@#dr1801-S-i {{non-type template argument refers to subobject '.i'}} } namespace dr1802 { // dr1802: 3.1 diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp index a17720c9267ccf..7fce6155169245 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp @@ -200,7 +200,9 @@ namespace bad_args { // cxx17-note@-3 {{reinterpret_cast}} X0<__builtin_constant_p(0) ? (int*)1 : (int*)1> x0d; // precxx17-error@-1 {{non-type template argument '(int *)1' is invalid}} - // cxx17-error@-2 {{non-type template argument refers to subobject '(int *)1'}} +#if __cplusplus == 201703L + // cxx17-error@-3 {{non-type template argument refers to subobject '(int *)1'}} +#endif } #endif // CPP11ONLY diff --git a/clang/test/CodeGenCXX/mangle-ms-templates.cpp b/clang/test/CodeGenCXX/mangle-ms-templates.cpp index cefe1e0cb5a550..ec4250edf314f7 100644 --- a/clang/test/CodeGenCXX/mangle-ms-templates.cpp +++ b/clang/test/CodeGenCXX/mangle-ms-templates.cpp @@ -2,6 +2,21 @@ // RUN: %clang_cc1 -std=c++11 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s // RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=i386-pc-win32 | FileCheck %s // RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s +// RUN: %clang_cc1 -std=c++20 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix CXX20-X64 %s + +// Check that array-to-pointer decay is mangled as the underlying declaration. +extern const char arr[4] = "foo"; +template<const char*> struct Decay1 {}; +// CHECK: "?decay1@@3U?$Decay1@$1?arr@@3QBDB@@A" +Decay1<arr> decay1; +#if __cplusplus >= 201702L +// Note that this mangling approach can lead to collisions. +template<const void*> struct Decay2 {}; +// CXX20-X64: "?decay2a@@3U?$Decay2@$1?arr@@3QBDB@@A" +Decay2<(const void*)arr> decay2a; +// CXX20-X64: "?decay2b@@3U?$Decay2@$1?arr@@3QBDB@@A" +Decay2<(const void*)&arr> decay2b; +#endif template<typename T> class Class { @@ -327,3 +342,36 @@ void fun_uint128(UInt128<(unsigned __int128)-1>) {} // X64: define {{.*}} @"?fun_uint128@@YAXU?$UInt128@$0DPPPPPPPPPPPPPPPAAAAAAAAAAAAAAAB@@@@Z"( void fun_uint128(UInt128<(unsigned __int128)9223372036854775807 * (unsigned __int128)9223372036854775807>) {} #endif + +#if __cplusplus >= 202002L + +template<float> struct Float {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$Float@$ADPIAAAAA@@@@Z"( +void f(Float<1.0f>) {} +template<auto> struct Auto {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$Auto@$MMADPIAAAAA@@@@Z"( +void f(Auto<1.0f>) {} + +struct S2 { + int arr[2][3]; + int i; + void fn(); +} s2; + +template<int&> struct TplSubobjectRef {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplSubobjectRef@$CC61?s2@@3US2@@Aarr@@00@01@@@@Z"( +void f(TplSubobjectRef<s2.arr[1][2]>) {} +template<int*> struct TplSubobjectPtr {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplSubobjectPtr@$CC61?s2@@3US2@@Aarr@@00@01@@@@Z"( +void f(TplSubobjectPtr<&s2.arr[1][2]>) {} + +struct Derived : S2 {}; + +template <int Derived::*> struct TplMemberPtr {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplMemberPtr@$0BI@@@@Z"( +void f(TplMemberPtr<(int Derived::*)&S2::i>) {} +template <void (Derived::*)()> struct TplMemberFnPtr {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplMemberFnPtr@$1?fn@S2@@QEAAXXZ@@@Z"( +void f(TplMemberFnPtr<(void (Derived::*)())&S2::fn>) {} + +#endif diff --git a/clang/test/CodeGenCXX/mangle-template.cpp b/clang/test/CodeGenCXX/mangle-template.cpp index 8415bacbb9fc6d..a4cb22739ac2a9 100644 --- a/clang/test/CodeGenCXX/mangle-template.cpp +++ b/clang/test/CodeGenCXX/mangle-template.cpp @@ -225,6 +225,16 @@ namespace test16 { namespace cxx20 { template<auto> struct A {}; template<typename T, T V> struct B {}; + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AILf3f800000EEE( + void f(A<1.0f>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AILd3ff0000000000000EEE( + void f(A<1.0>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AILe3fff8000000000000000EEE( + void f(A<1.0l>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXtlCiLi0ELi1EEEEE( + void f(A<1i>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXtlCdLd0000000000000000ELd3ff0000000000000EEEEE( + void f(A<1.0i>) {} int x; // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1xEEEEE( @@ -244,7 +254,24 @@ namespace cxx20 { // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadL_ZNS_1xEEEEE( void f(B<const void*, (const void*)&x>) {} - struct Q { int x; }; + struct Q { int x; } q; + + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadsoiL_ZNS_1qEEEEEE( + void f(A<&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPiXadsoiL_ZNS_1qEEEEEE( + void f(B<int*, &q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadsoKiL_ZNS_1qEEEEEE( + void f(A<(const int*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKiXadsoS1_L_ZNS_1qEEEEEE + void f(B<const int*, (const int*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPvadsoiL_ZNS_1qEEEEEE( + void f(A<(void*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPvXadsoiL_ZNS_1qEEEEEE( + void f(B<void*, (void*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPKvadsoiL_ZNS_1qEEEEEE( + void f(A<(const void*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadsoiL_ZNS_1qEEEEEE( + void f(B<const void*, (const void*)&q.x>) {} // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1Q1xEEEEE( void f(A<&Q::x>) {} @@ -254,6 +281,17 @@ namespace cxx20 { void f(A<(const int Q::*)&Q::x>) {} // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1QEKiXadL_ZNS1_1xEEEEE( void f(B<const int Q::*, (const int Q::*)&Q::x>) {} + + struct R : Q {}; + + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXmcMNS_1REiadL_ZNS_1Q1xEEEEEE( + void f(A<(int R::*)&Q::x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1REiXmcS2_adL_ZNS_1Q1xEEEEEE( + void f(B<int R::*, (int R::*)&Q::x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXmcMNS_1REKiadL_ZNS_1Q1xEEEEEE( + void f(A<(const int R::*)&Q::x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1REKiXmcS3_adL_ZNS_1Q1xEEEEEE( + void f(B<const int R::*, (const int R::*)&Q::x>) {} } #endif diff --git a/clang/test/CodeGenCXX/template-arguments.cpp b/clang/test/CodeGenCXX/template-arguments.cpp new file mode 100644 index 00000000000000..14a454937122aa --- /dev/null +++ b/clang/test/CodeGenCXX/template-arguments.cpp @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - -triple x86_64-linux -DCONSTEXPR= | FileCheck %s +// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - -triple x86_64-linux -DCONSTEXPR=constexpr | FileCheck %s --check-prefix=CONST + +template<typename T> CONSTEXPR T id(T v) { return v; } +template<auto V> auto value = id(V); + +// CHECK: call {{.*}} @_Z2idIiET_S0_(i32 noundef 1) +// CONST: @_Z5valueILi1EE = weak_odr {{.*}} i32 1, +template int value<1>; + +// CHECK: call {{.*}} @_Z2idIyET_S0_(i64 noundef -1) +// CONST: @_Z5valueILy18446744073709551615EE = weak_odr {{.*}} i64 -1, +template unsigned long long value<-1ULL>; + +// CHECK: call {{.*}} @_Z2idIfET_S0_(float noundef 1.000000e+00) +// CONST: @_Z5valueILf3f800000EE = weak_odr {{.*}} float 1.000000e+00, +template float value<1.0f>; +// CHECK: call {{.*}} @_Z2idIdET_S0_(double noundef 1.000000e+00) +// CONST: @_Z5valueILd3ff0000000000000EE = weak_odr {{.*}} double 1.000000e+00, +template double value<1.0>; + +enum E{ E1, E2}; + +// CHECK: call {{.*}} @_Z2idI1EET_S1_(i32 noundef 1) +// CONST: @_Z5valueIL1E1EE = weak_odr {{.*}} i32 1, +template E value<E2>; + +int n; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @n) +// CONST: @_Z5valueIXadL_Z1nEEE = weak_odr {{.*}} ptr @n, +template int *value<&n>; + +struct A { int a[3]; } a; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @a) +// CONST: @_Z5valueIXadsoiL_Z1aEEEE = weak_odr {{.*}} ptr @a, +template int *value<&a.a[0]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef getelementptr (i8, ptr @a, i64 4)) +// CONST: @_Z5valueIXadsoiL_Z1aE4EEE = weak_odr {{.*}} ptr getelementptr (i8, ptr @a, i64 4), +template int *value<&a.a[1]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef getelementptr (i8, ptr @a, i64 8)) +// CONST: @_Z5valueIXadsoiL_Z1aE8EEE = weak_odr {{.*}} ptr getelementptr (i8, ptr @a, i64 8), +template int *value<&a.a[2]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef getelementptr (i8, ptr @a, i64 12)) +// CONST: @_Z5valueIXadsoiL_Z1aE12pEEE = weak_odr {{.*}} ptr getelementptr (i8, ptr @a, i64 12), +template int *value<&a.a[3]>; + +union U { + int x, y; + union { + int x, y; + } internal; +} u; + +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @u) +// CONST: @_Z5valueIXadsoiL_Z1uE_EEE = weak_odr {{.*}} ptr @u, +template int *value<&u.x>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @u) +// CONST: @_Z5valueIXadsoiL_Z1uE_0EEE = weak_odr {{.*}} ptr @u, +template int *value<&u.y>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @u) +// CONST: @_Z5valueIXadsoiL_Z1uE_1_0EEE = weak_odr {{.*}} ptr @u, +template int *value<&u.internal.y>; + +struct B { int x, y; }; +// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 0) +// CONST: @_Z5valueIXadL_ZN1B1xEEEE = weak_odr {{.*}} i64 0, +template int B::*value<&B::x>; +// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 4) +// CONST: @_Z5valueIXadL_ZN1B1yEEEE = weak_odr {{.*}} i64 4, +template int B::*value<&B::y>; + +struct C : A, B { int z; }; +// CHECK: call {{.*}} @_Z2idIM1CiET_S2_(i64 12) +// CONST: @_Z5valueIXmcM1CiadL_ZN1B1xEE12EEE = weak_odr {{.*}} i64 12, +template int C::*value<(int C::*)&B::x>; +// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 8) +// CONST: @_Z5valueIXmcM1BiadL_ZN1C1zEEn12EEE = weak_odr {{.*}} i64 8, +template int B::*value<(int B::*)&C::z>; + +// CHECK: store i32 1, +// CHECK: store i32 2, +// CHECK: load i64, +// CHECK: call {{.*}} @_Z2idICiET_S1_(i64 noundef % +// CONST: @_Z5valueIXtlCiLi1ELi2EEEE = weak_odr {{.*}} { i32, i32 } { i32 1, i32 2 }, +template _Complex int value<1 + 2j>; + +// CHECK: store float 1.000000e+00, +// CHECK: store float 2.000000e+00, +// CHECK: load <2 x float>, +// CHECK: call {{.*}} @_Z2idICfET_S1_(<2 x float> noundef % +// CONST: @_Z5valueIXtlCfLf3f800000ELf40000000EEEE = weak_odr {{.*}} { float, float } { float 1.000000e+00, float 2.000000e+00 }, +template _Complex float value<1.0f + 2.0fj>; + +using V3i __attribute__((ext_vector_type(3))) = int; +// CHECK: call {{.*}} @_Z2idIDv3_iET_S1_(<3 x i32> noundef <i32 1, i32 2, i32 3>) +// CONST: @_Z5valueIXtlDv3_iLi1ELi2ELi3EEEE = weak_odr {{.*}} <3 x i32> <i32 1, i32 2, i32 3> +template V3i value<V3i{1, 2, 3}>; + +using V3f [[gnu::vector_size(12)]] = float; +// CHECK: call {{.*}} @_Z2idIDv3_fET_S1_(<3 x float> noundef <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>) +// CONST: @_Z5valueIXtlDv3_fLf3f800000ELf40000000ELf40400000EEEE = weak_odr {{.*}} <3 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00> +template V3f value<V3f{1, 2, 3}>; + + +template <int& i> +void setByRef() { + i = 1; +} + +void callSetByRefWithSubobject() { + // CHECK: store i32 1, ptr getelementptr (i8, ptr @a, i64 4) + setByRef<a.a[1]>(); +} diff --git a/clang/test/Index/USR/structural-value-tpl-arg.cpp b/clang/test/Index/USR/structural-value-tpl-arg.cpp new file mode 100644 index 00000000000000..8130012451691c --- /dev/null +++ b/clang/test/Index/USR/structural-value-tpl-arg.cpp @@ -0,0 +1,23 @@ +// RUN: c-index-test -test-load-source-usrs local -std=c++20 -- %s | FileCheck %s + +// Check USRs of template specializations with structural NTTP values. + +template <auto> struct Tpl{}; + +struct { + int n; +} s; + +void fn1(Tpl<1.5>); +// CHECK: fn1#$@S@Tpl>#Sd[[#HASH:]]# +void fn2(Tpl<1.7>); +// CHECK-NOT: [[#HASH]] +void fn1(Tpl<1.5>) {} +// CHECK: fn1#$@S@Tpl>#Sd[[#HASH]]# + +void fn(Tpl<&s.n>); +// CHECK: #S*I[[#HASH:]]# +void fn(Tpl<(void*)&s.n>); +// CHECK: #S*v[[#HASH]]# +void fn(Tpl<&s.n>) {} +// CHECK: #S*I[[#HASH]]# diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp index 220ef767df849a..fa8b2c81ab46e1 100644 --- a/clang/test/Modules/odr_hash.cpp +++ b/clang/test/Modules/odr_hash.cpp @@ -13,8 +13,8 @@ // RUN: cat %s >> %t/Inputs/second.h // Test that each header can compile -// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++1z %t/Inputs/first.h -// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++1z %t/Inputs/second.h +// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++20 %t/Inputs/first.h +// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++20 %t/Inputs/second.h // Build module map file // RUN: echo "module FirstModule {" >> %t/Inputs/module.modulemap @@ -25,7 +25,7 @@ // RUN: echo "}" >> %t/Inputs/module.modulemap // Run test -// RUN: %clang_cc1 -triple x86_64-linux-gnu -x c++ -std=c++1z \ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -x c++ -std=c++20 \ // RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ // RUN: -I%t/Inputs -verify %s @@ -2093,6 +2093,193 @@ struct S21 { S21 s21; #endif +#if defined(FIRST) +struct S22 { + template <double> void f(){}; + template <> void f<1.5>(){}; +}; +#elif defined(SECOND) +struct S22 { + template <double> void f(){}; + template <> void f<1.7>(){}; +}; +#else +S22 s22; +// expected-error@second.h:* {{'TemplateArgument::S22' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with 1.700000e+00 for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with 1.500000e+00 for 1st template argument}} +#endif + +#if defined(FIRST) +struct S23 { + template <double> void f(){}; + template <> void f<2.7>(){}; +}; +#elif defined(SECOND) +struct S23 { + template <double> void f(){}; + template <> void f<2.7>(){}; +}; +#else +S23 s23; +#endif + +#if defined(FIRST) || defined(SECOND) +struct Composite { + int n1[4]; + int n2[4]; +}; +extern Composite composite; +#endif + +#if defined(FIRST) +struct S24 { + template <int&> void f(){}; + template <> void f<composite.n1[1]>(){}; +}; +#elif defined(SECOND) +struct S24 { + template <int&> void f(){}; + template <> void f<composite.n1[2]>(){}; +}; +#else +S24 s24; +// expected-error@second.h:* {{'TemplateArgument::S24' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with composite.n1[2] for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with composite.n1[1] for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S25 { + template <int&> void f(); + template <> void f<composite.n1[2]>(); +}; +#else +S25 s25; +#endif + +#if defined(FIRST) +struct S26 { + template <int*> void f(){}; + template <> void f<&composite.n1[4]>(){}; // Past-the-end pointer. +}; +#elif defined(SECOND) +struct S26 { + template <int*> void f(){}; + template <> void f<&composite.n2[0]>(){}; +}; +#else +S26 s26; +// expected-error@second.h:* {{'TemplateArgument::S26' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with &composite.n2[0] for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with &composite.n1[4] for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +union Union { + int i1; + int i2; +}; +extern Union u; +#endif + +#if defined(FIRST) +struct S27 { + template <int&> void f(){}; + template <> void f<u.i1>(){}; +}; +#elif defined(SECOND) +struct S27 { + template <int&> void f(){}; + template <> void f<u.i2>(){}; +}; +#else +S27 s27; +// expected-error@second.h:* {{'TemplateArgument::S27' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with u.i2 for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with u.i1 for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S28 { + template <int&> void f(){}; + template <> void f<u.i1>(){}; +}; +#else +S28 s28; +#endif + +#if defined(FIRST) || defined(SECOND) +struct A { + int a; +}; +struct B : A {}; +struct C : A {}; +struct D : B, C {}; +#endif + +#if defined(FIRST) +struct S29 { + template <int D::*> void f(){}; + template <> void f<(int D::*)(int B::*)&A::a>(){}; +}; +#elif defined(SECOND) +struct S29 { + template <int D::*> void f(){}; + template <> void f<(int D::*)(int C::*)&A::a>(){}; +}; +#else +S29 s29; +// expected-error@second.h:* {{'TemplateArgument::S29' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with &A::a for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with &A::a for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S30 { + template <int D::*> void f(){}; + template <> void f<(int D::*)(int B::*)&A::a>(){}; +}; +#else +S30 s30; +#endif + +#if defined(FIRST) +struct S31 { + template <auto*> void f(){}; + template <> void f<&composite.n1[2]>(){}; +}; +#elif defined(SECOND) +struct S31 { + template <auto*> void f(){}; + template <> void f<(void*)&composite.n1[2]>(){}; +}; +#else +S31 s31; +// expected-error@second.h:* {{'TemplateArgument::S31' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with &composite.n1[2] for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with &composite.n1[2] for 1st template argument}} +#endif + +#if defined(FIRST) +struct S32 { + template <int*> void f(){}; + template <> void f<__builtin_constant_p(0) ? (int*)1 : (int*)1>(){}; +}; +#elif defined(SECOND) +struct S32 { + template <int*> void f(){}; + template <> void f<__builtin_constant_p(0) ? (int*)2 : (int*)2>(){}; +}; +#else +S32 s32; +// expected-error@second.h:* {{'TemplateArgument::S32' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with (int *)2 for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with (int *)1 for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S33 { + template <int*> void f(){}; + template <> void f<__builtin_constant_p(0) ? (int*)1 : (int*)1>(){}; +}; +#else +S33 s33; +#endif + #define DECLS \ OneClass<int> a; \ OneInt<1> b; \ diff --git a/clang/test/SemaCXX/warn-bool-conversion.cpp b/clang/test/SemaCXX/warn-bool-conversion.cpp index 7b671c52b5b9c0..c81d52d864f2d2 100644 --- a/clang/test/SemaCXX/warn-bool-conversion.cpp +++ b/clang/test/SemaCXX/warn-bool-conversion.cpp @@ -186,6 +186,7 @@ namespace macros { } } +#if __cplusplus < 201703L namespace Template { // FIXME: These cases should not warn. template<int *p> void f() { if (p) {} } // expected-warning 2{{will always evaluate to 'true'}} expected-cxx11-warning {{implicit conversion of nullptr}} @@ -205,3 +206,4 @@ namespace Template { #endif template void h<d>(); } +#endif // __cplusplus < 201703L diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index 22788e1c1ffb3a..ae06055bf52651 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -2,7 +2,7 @@ template<typename T, T val> struct A {}; -template<typename T, typename U> constexpr bool is_same = false; // expected-note +{{here}} +template<typename T, typename U> constexpr bool is_same = false; template<typename T> constexpr bool is_same<T, T> = true; namespace String { @@ -84,34 +84,32 @@ namespace PtrMem { constexpr int B::*b = &B::b; constexpr int C::*cb = b; constexpr int D::*db = b; - constexpr int E::*ecb = cb; // expected-note +{{here}} - constexpr int E::*edb = db; // expected-note +{{here}} + constexpr int E::*ecb = cb; + constexpr int E::*edb = db; constexpr int E::*e = &E::e; constexpr int D::*de = (int D::*)e; constexpr int C::*ce = (int C::*)e; - constexpr int B::*bde = (int B::*)de; // expected-note +{{here}} - constexpr int B::*bce = (int B::*)ce; // expected-note +{{here}} + constexpr int B::*bde = (int B::*)de; + constexpr int B::*bce = (int B::*)ce; - // FIXME: This should all be accepted, but we don't yet have a representation - // nor mangling for this form of template argument. using Ab = A<int B::*, b>; using Ab = A<int B::*, &B::b>; - using Abce = A<int B::*, bce>; // expected-error {{not supported}} - using Abde = A<int B::*, bde>; // expected-error {{not supported}} - static_assert(!is_same<Ab, Abce>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Ab, Abde>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Abce, Abde>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}} - static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); // expected-error {{undeclared}} expected-error {{not supported}} + using Abce = A<int B::*, bce>; + using Abde = A<int B::*, bde>; + static_assert(!is_same<Ab, Abce>, ""); + static_assert(!is_same<Ab, Abde>, ""); + static_assert(!is_same<Abce, Abde>, ""); + static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); using Ae = A<int E::*, e>; using Ae = A<int E::*, &E::e>; - using Aecb = A<int E::*, ecb>; // expected-error {{not supported}} - using Aedb = A<int E::*, edb>; // expected-error {{not supported}} - static_assert(!is_same<Ae, Aecb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Ae, Aedb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Aecb, Aedb>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}} - static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); // expected-error {{undeclared}} expected-error {{not supported}} + using Aecb = A<int E::*, ecb>; + using Aedb = A<int E::*, edb>; + static_assert(!is_same<Ae, Aecb>, ""); + static_assert(!is_same<Ae, Aedb>, ""); + static_assert(!is_same<Aecb, Aedb>, ""); + static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); using An = A<int E::*, nullptr>; using A0 = A<int E::*, (int E::*)0>; @@ -205,9 +203,9 @@ namespace Auto { struct Y : X {}; void type_affects_identity(B<&X::n>) {} - void type_affects_identity(B<(int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}} + void type_affects_identity(B<(int Y::*)&X::n>) {} void type_affects_identity(B<(const int X::*)&X::n>) {} - void type_affects_identity(B<(const int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}} + void type_affects_identity(B<(const int Y::*)&X::n>) {} // A case where we need to do auto-deduction, and check whether the // resulting dependent types match during partial ordering. These diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp index 982f6ec2215708..b5b8cadc909ce0 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp @@ -8,8 +8,8 @@ namespace std { // floating-point arguments template<float> struct Float {}; -using F1 = Float<1.0f>; // FIXME expected-error {{sorry}} -using F1 = Float<2.0f / 2>; // FIXME expected-error {{sorry}} +using F1 = Float<1.0f>; +using F1 = Float<2.0f / 2>; struct S { int n[3]; } s; // expected-note 1+{{here}} union U { int a, b; } u; @@ -17,24 +17,28 @@ int n; // expected-note 1+{{here}} // pointers to subobjects template<int *> struct IntPtr {}; -using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}} -using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}} +using IPn = IntPtr<&n + 1>; +using IPn = IntPtr<&n + 1>; -using IP2 = IntPtr<&s.n[2]>; // FIXME expected-error {{refers to subobject}} -using IP2 = IntPtr<s.n + 2>; // FIXME expected-error {{refers to subobject}} +using IPn2 = IntPtr<&n + 2>; // expected-error {{not a constant expression}} expected-note {{cannot refer to element 2 of non-array object}} -using IP3 = IntPtr<&s.n[3]>; // FIXME expected-error {{refers to subobject}} -using IP3 = IntPtr<s.n + 3>; // FIXME expected-error {{refers to subobject}} +using IP2 = IntPtr<&s.n[2]>; +using IP2 = IntPtr<s.n + 2>; + +using IP3 = IntPtr<&s.n[3]>; +using IP3 = IntPtr<s.n + 3>; + +using IP5 = IntPtr<&s.n[5]>; // expected-error {{not a constant expression}} expected-note {{cannot refer to element 5 of array of 3 elements}} template<int &> struct IntRef {}; -using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} -using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} +using IRn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} +using IRn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} -using IP2 = IntRef<s.n[2]>; // FIXME expected-error {{refers to subobject}} -using IP2 = IntRef<*(s.n + 2)>; // FIXME expected-error {{refers to subobject}} +using IR2 = IntRef<s.n[2]>; +using IR2 = IntRef<*(s.n + 2)>; -using IP3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} -using IP3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} +using IR3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} +using IR3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} // classes template<S> struct Struct {}; @@ -48,12 +52,12 @@ using U1 = Union<U{.b = 1}>; // expected-error {{different types}} // miscellaneous scalar types template<_Complex int> struct ComplexInt {}; -using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}} -using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}} +using CI = ComplexInt<1 + 3i>; +using CI = ComplexInt<3i + 1>; template<_Complex float> struct ComplexFloat {}; -using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}} -using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}} +using CF = ComplexFloat<1.0f + 3.0fi>; +using CF = ComplexFloat<3.0fi + 1.0f>; namespace ClassNTTP { struct A { // expected-note 2{{candidate}} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 841522a0f4788f..6adaf5d8b7ab67 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1582,6 +1582,11 @@ bool CursorVisitor::VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL) { return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); return false; + case TemplateArgument::StructuralValue: + if (Expr *E = TAL.getSourceStructuralValueExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + case TemplateArgument::NullPtr: if (Expr *E = TAL.getSourceNullPtrExpression()) return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index fd03c48ba1a42a..978adac5521aaa 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -1466,6 +1466,9 @@ enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C, return CXTemplateArgumentKind_NullPtr; case TemplateArgument::Integral: return CXTemplateArgumentKind_Integral; + case TemplateArgument::StructuralValue: + // FIXME: Expose these values. + return CXTemplateArgumentKind_Invalid; case TemplateArgument::Template: return CXTemplateArgumentKind_Template; case TemplateArgument::TemplateExpansion: diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 197726f3aa3eee..a48f35eb504837 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -637,13 +637,21 @@ <h2 id="cxx20">C++20 implementation status</h2> </tr> <!-- Rapperswil papers --> <tr> - <td rowspan="2">Class types as non-type template parameters</td> + <td>Class types as non-type template parameters</td> <td><a href="https://wg21.link/p0732r2">P0732R2</a></td> - <td rowspan="2" class="partial" align="center">Partial</td> + <td class="full" align="center">Clang 12</td> + </tr> + <tr> <!-- from Belfast --> + <td>Generalized non-type template parameters of scalar type</td> + <td><a href="https://wg21.link/p1907r1">P1907R1</a></td> + <td class="partial" align="center"> + <details> + <summary>Clang 18 (Partial)</summary> + Reference type template arguments referring to instantiation-dependent objects and subobjects + (i.e. declared inside a template but neither type- nor value-dependent) aren't fully supported. + </details> + </td> </tr> - <tr> <!-- from Belfast --> - <td><a href="https://wg21.link/p1907r1">P1907R1</a></td> - </tr> <tr> <td>Destroying operator delete</td> <td><a href="https://wg21.link/p0722r3">P0722R3</a></td> diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index ed1dec85d48406..392d333c23a447 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -875,6 +875,7 @@ enum TemplateArgumentKind { eTemplateArgumentKindExpression, eTemplateArgumentKindPack, eTemplateArgumentKindNullPtr, + eTemplateArgumentKindStructuralValue, }; /// Type of match to be performed when looking for a formatter for a data type. diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 797df8c098af10..5dcf4ce030ee2c 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -7168,6 +7168,9 @@ TypeSystemClang::GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, case clang::TemplateArgument::Pack: return eTemplateArgumentKindPack; + + case clang::TemplateArgument::StructuralValue: + return eTemplateArgumentKindStructuralValue; } llvm_unreachable("Unhandled clang::TemplateArgument::ArgKind"); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits