Author: Richard Smith Date: 2021-01-18T21:05:01-08:00 New Revision: 4b574008aef5a7235c1f894ab065fe300d26e786
URL: https://github.com/llvm/llvm-project/commit/4b574008aef5a7235c1f894ab065fe300d26e786 DIFF: https://github.com/llvm/llvm-project/commit/4b574008aef5a7235c1f894ab065fe300d26e786.diff LOG: [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. This incorporates the following follow-on commits that were also reverted: 7e84aa1b81e72d44bcc58ffe1731bfc7abb73ce0 by Simon Pilgrim ed13d8c66781b50ff007cb089c5905f9bb9e8af2 by me 95c7b6cadbc9a3d4376ef44edbeb3c8bb5b8d7fc by Sam McCall 430d5d8429473c2b10b109991d7577a3cea41140 by Dave Zarzycki Added: clang/test/CodeGenCXX/template-arguments.cpp Modified: clang-tools-extra/clangd/DumpAST.cpp clang-tools-extra/clangd/FindTarget.cpp clang-tools-extra/clangd/index/remote/Client.cpp clang/include/clang/AST/ASTContext.h clang/include/clang/AST/PropertiesBase.td clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/AST/TemplateArgumentVisitor.h clang/include/clang/AST/TemplateBase.h clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/AST/ASTContext.cpp clang/lib/AST/ASTImporter.cpp clang/lib/AST/ASTStructuralEquivalence.cpp clang/lib/AST/Decl.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/MicrosoftMangle.cpp clang/lib/AST/ODRHash.cpp clang/lib/AST/StmtProfile.cpp clang/lib/AST/TemplateBase.cpp clang/lib/AST/TypeLoc.cpp clang/lib/CodeGen/CGDebugInfo.cpp clang/lib/CodeGen/CGExprConstant.cpp clang/lib/Index/USRGeneration.cpp clang/lib/Sema/SemaLookup.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateVariadic.cpp clang/lib/Sema/TreeTransform.h clang/lib/Serialization/ASTReader.cpp clang/lib/Serialization/ASTWriter.cpp clang/test/CodeGenCXX/mangle-ms-templates.cpp clang/test/CodeGenCXX/mangle-template.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx17.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp clang/tools/libclang/CIndex.cpp clang/tools/libclang/CXCursor.cpp lldb/include/lldb/lldb-enumerations.h lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index 588bcfcf2424..bf7675e7d949 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(UncommonValue); #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 84316659daad..98ef8b3b6d76 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -1079,6 +1079,7 @@ class ExplicitReferenceCollector case TemplateArgument::Pack: case TemplateArgument::Type: case TemplateArgument::Expression: + case TemplateArgument::UncommonValue: break; // Handled by VisitType and VisitExpression. }; return RecursiveASTVisitor::TraverseTemplateArgumentLoc(A); diff --git a/clang-tools-extra/clangd/index/remote/Client.cpp b/clang-tools-extra/clangd/index/remote/Client.cpp index b09dbf915e46..a153a8812baf 100644 --- a/clang-tools-extra/clangd/index/remote/Client.cpp +++ b/clang-tools-extra/clangd/index/remote/Client.cpp @@ -152,7 +152,8 @@ class IndexClient : public clangd::SymbolIndex { }); } - llvm::unique_function<bool(llvm::StringRef) const> indexedFiles() const { + llvm::unique_function<bool(llvm::StringRef) const> + indexedFiles() const override { // FIXME: For now we always return "false" regardless of whether the file // was indexed or not. A possible implementation could be based on // the idea that we do not want to send a request at every diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 0c5d82b3e9aa..a9bfdb4d5fa5 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2818,8 +2818,8 @@ class ASTContext : public RefCountedBase<ASTContext> { /// for destruction. template <typename T> void addDestruction(T *Ptr) const { if (!std::is_trivially_destructible<T>::value) { - auto DestroyPtr = [](void *V) { static_cast<T *>(V)->~T(); }; - AddDeallocation(DestroyPtr, Ptr); + auto DestroyPtr = [](void *V) { ((T*)V)->~T(); }; + AddDeallocation(DestroyPtr, (void*)Ptr); } } diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index a087cb406b29..98229eeedaa5 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -758,6 +758,17 @@ let Class = PropertyTypeCase<TemplateArgument, "Integral"> in { return TemplateArgument(ctx, value, type); }]>; } +let Class = PropertyTypeCase<TemplateArgument, "UncommonValue"> in { + def : Property<"value", APValue> { + let Read = [{ node.getAsUncommonValue() }]; + } + def : Property<"type", QualType> { + let Read = [{ node.getUncommonValueType() }]; + } + def : Creator<[{ + return TemplateArgument(ctx, type, value); + }]>; +} 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 505ea700fd0e..1426e569eabe 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -768,6 +768,7 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateArgument( case TemplateArgument::Declaration: case TemplateArgument::Integral: case TemplateArgument::NullPtr: + case TemplateArgument::UncommonValue: return true; case TemplateArgument::Type: @@ -801,6 +802,7 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLoc( case TemplateArgument::Declaration: case TemplateArgument::Integral: case TemplateArgument::NullPtr: + case TemplateArgument::UncommonValue: return true; case TemplateArgument::Type: { diff --git a/clang/include/clang/AST/TemplateArgumentVisitor.h b/clang/include/clang/AST/TemplateArgumentVisitor.h index 190aa97adf45..8c0da70b25eb 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(UncommonValue); DISPATCH(Template); DISPATCH(TemplateExpansion); DISPATCH(Expression); @@ -59,6 +60,7 @@ class Base { VISIT_METHOD(Declaration); VISIT_METHOD(NullPtr); VISIT_METHOD(Integral); + VISIT_METHOD(UncommonValue); 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 5fbb25c315cf..d2a24da3c842 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -51,6 +51,7 @@ template <> struct PointerLikeTypeTraits<clang::Expr *> { namespace clang { +class APValue; class ASTContext; class DiagnosticBuilder; class Expr; @@ -82,6 +83,12 @@ 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. + UncommonValue, + /// The template argument is a template name that was provided for a /// template template parameter. Template, @@ -125,6 +132,11 @@ class TemplateArgument { }; void *Type; }; + struct V { + unsigned Kind; + const APValue *Value; + void *Type; + }; struct A { unsigned Kind; unsigned NumArgs; @@ -142,6 +154,7 @@ class TemplateArgument { union { struct DA DeclArg; struct I Integer; + struct V Value; struct A Args; struct TA TemplateArg; struct TV TypeOrValue; @@ -157,9 +170,8 @@ class TemplateArgument { TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr()); } - /// 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) { assert(D && "Expected decl"); DeclArg.Kind = Declaration; @@ -169,7 +181,11 @@ class TemplateArgument { /// 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); + + /// Construct a template argument from an arbitrary constant value. + TemplateArgument(const ASTContext &Ctx, QualType Type, const APValue &Value); /// Construct an integral constant template argument with the same /// value as Other but a diff erent type. @@ -340,6 +356,16 @@ class TemplateArgument { Integer.Type = T.getAsOpaquePtr(); } + /// Get the value of an UncommonValue. + const APValue &getAsUncommonValue() const { + return *Value.Value; + } + + /// Get the type of an UncommonValue. + QualType getUncommonValueType() 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; @@ -484,6 +510,7 @@ class TemplateArgumentLoc { assert(Argument.getKind() == TemplateArgument::NullPtr || Argument.getKind() == TemplateArgument::Integral || Argument.getKind() == TemplateArgument::Declaration || + Argument.getKind() == TemplateArgument::UncommonValue || Argument.getKind() == TemplateArgument::Expression); } @@ -542,6 +569,11 @@ class TemplateArgumentLoc { return LocInfo.getAsExpr(); } + Expr *getSourceUncommonValueExpression() const { + assert(Argument.getKind() == TemplateArgument::UncommonValue); + return LocInfo.getAsExpr(); + } + NestedNameSpecifierLoc getTemplateQualifierLoc() const { if (Argument.getKind() != TemplateArgument::Template && Argument.getKind() != TemplateArgument::TemplateExpansion) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e93657898f58..1fb8dbd32bad 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4695,8 +4695,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 diff erence / 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">; @@ -4748,9 +4746,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 diff erent class is not supported yet">; def ext_template_arg_extra_parens : ExtWarn< "address non-type template argument cannot be surrounded by parentheses">; def warn_cxx98_compat_template_arg_extra_parens : Warning< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7cdfe24a3a3f..4e93361ba6f7 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7735,8 +7735,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 0190573fe36e..d396f81188df 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5941,6 +5941,11 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { case TemplateArgument::Integral: return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); + case TemplateArgument::UncommonValue: + return TemplateArgument(*this, + getCanonicalType(Arg.getUncommonValueType()), + Arg.getAsUncommonValue()); + case TemplateArgument::Type: return TemplateArgument(getCanonicalType(Arg.getAsType())); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 5c6aa5d3c015..416764d48543 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -808,6 +808,17 @@ ASTNodeImporter::import(const TemplateArgument &From) { return TemplateArgument(*ToTypeOrErr, /*isNullPtr*/true); } + case TemplateArgument::UncommonValue: { + ExpectedType ToTypeOrErr = import(From.getUncommonValueType()); + if (!ToTypeOrErr) + return ToTypeOrErr.takeError(); + Expected<APValue> ToValueOrErr = import(From.getAsUncommonValue()); + if (!ToValueOrErr) + return ToValueOrErr.takeError(); + return TemplateArgument(Importer.getToContext(), *ToTypeOrErr, + *ToValueOrErr); + } + case TemplateArgument::Template: { Expected<TemplateName> ToTemplateOrErr = import(From.getAsTemplate()); if (!ToTemplateOrErr) diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index d004e443ae06..f7696bc7c921 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -565,6 +565,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return IsStructurallyEquivalent(Context, Arg1.getAsExpr(), Arg2.getAsExpr()); + case TemplateArgument::UncommonValue: + // FIXME: Do we need to customize the comparison? + return Arg1.structurallyEquals(Arg2); + case TemplateArgument::Pack: if (Arg1.pack_size() != Arg2.pack_size()) return false; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 3cea3c23b527..d636d0e430d3 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -342,6 +342,10 @@ LinkageComputer::getLVForTemplateArgumentList(ArrayRef<TemplateArgument> Args, LV.merge(getTypeLinkageAndVisibility(Arg.getNullPtrType())); continue; + case TemplateArgument::UncommonValue: + LV.merge(getLVForValue(Arg.getAsUncommonValue(), 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 084a0b0c5bf0..e7db8e2baa84 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4079,10 +4079,28 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { mangleExpression(cast<CXXStdInitializerListExpr>(E)->getSubExpr(), Arity); break; - case Expr::SubstNonTypeTemplateParmExprClass: - mangleExpression(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(), - Arity); + case Expr::SubstNonTypeTemplateParmExprClass: { + // Mangle a substituted parameter the same way we mangle the template + // argument. + // As proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/111. + 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()); + if (CE->hasAPValueResult()) + mangleValueInTemplateArg(ParamType, CE->getResultAsAPValue(), false, + /*NeedExactType=*/true); + else + mangleValueInTemplateArg(ParamType, CE->getAPValueResult(), false, + /*NeedExactType=*/true); + } else { + // The remaining cases all happen to be substituted with expressions that + // mangle the same as a corresponding template argument anyway. + mangleExpression(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(), + Arity); + } break; + } case Expr::UserDefinedLiteralClass: // We follow g++'s approach of mangling a UDL as a call to the literal @@ -5039,6 +5057,10 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A, bool NeedExactType) { mangleNullPointer(A.getNullPtrType()); break; } + case TemplateArgument::UncommonValue: + mangleValueInTemplateArg(A.getUncommonValueType(), A.getAsUncommonValue(), + /*TopLevel=*/true, NeedExactType); + break; case TemplateArgument::Pack: { // <template-arg> ::= J <template-arg>* E Out << 'J'; @@ -5373,7 +5395,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 df6c566abc7d..16e0aa2ae466 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -1575,6 +1575,17 @@ void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, cast<NonTypeTemplateParmDecl>(Parm), T); break; } + case TemplateArgument::UncommonValue: + Out << "$"; + if (cast<NonTypeTemplateParmDecl>(Parm) + ->getType() + ->getContainedDeducedType()) { + Out << "M"; + mangleType(TA.getNonTypeTemplateArgumentType(), SourceRange(), QMM_Drop); + } + mangleTemplateArgValue(TA.getUncommonValueType(), TA.getAsUncommonValue(), + /*WithScalarType=*/false); + break; case TemplateArgument::Expression: mangleExpression(TA.getAsExpr(), cast<NonTypeTemplateParmDecl>(Parm)); break; diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 735bcff8f113..92e3bc27fca0 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -169,6 +169,8 @@ void ODRHash::AddTemplateArgument(TemplateArgument TA) { break; case TemplateArgument::NullPtr: case TemplateArgument::Integral: + case TemplateArgument::UncommonValue: + // FIXME: Include a representation of these arguments. break; case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index de9de6ff463c..569e3b06f3b2 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2208,6 +2208,12 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { Arg.getAsIntegral().Profile(ID); break; + case TemplateArgument::UncommonValue: + VisitType(Arg.getUncommonValueType()); + // FIXME: Do we need to recursively decompose this ourselves? + Arg.getAsUncommonValue().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 8cbb595b5bd1..85caee1a4911 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -84,8 +84,8 @@ static void printIntegral(const TemplateArgument &TemplArg, // TemplateArgument Implementation //===----------------------------------------------------------------------===// -TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, - QualType Type) { +TemplateArgument::TemplateArgument(const ASTContext &Ctx, + const llvm::APSInt &Value, QualType Type) { Integer.Kind = Integral; // Copy the APSInt value into our decomposed form. Integer.BitWidth = Value.getBitWidth(); @@ -103,6 +103,45 @@ TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, Integer.Type = Type.getAsOpaquePtr(); } +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 'uncommon' representation. + return nullptr; +} + +TemplateArgument::TemplateArgument(const ASTContext &Ctx, QualType Type, + const APValue &V) { + if (Type->isIntegralOrEnumerationType() && V.isInt()) + *this = TemplateArgument(Ctx, V.getInt(), Type); + else if ((V.isLValue() && V.isNullPointer()) || + (V.isMemberPointer() && !V.getMemberPointerDecl())) + *this = TemplateArgument(Type, /*isNullPtr=*/true); + else if (const ValueDecl *VD = getAsSimpleValueDeclRef(Ctx, Type, V)) + // FIXME: The Declaration form should expose a const ValueDecl*. + *this = TemplateArgument(const_cast<ValueDecl*>(VD), Type); + else { + Value.Kind = UncommonValue; + Value.Value = new (Ctx) APValue(V); + Ctx.addDestruction(Value.Value); + Value.Type = Type.getAsOpaquePtr(); + } +} + TemplateArgument TemplateArgument::CreatePackCopy(ASTContext &Context, ArrayRef<TemplateArgument> Args) { @@ -134,6 +173,7 @@ TemplateArgumentDependence TemplateArgument::getDependence() const { case NullPtr: case Integral: case Declaration: + case UncommonValue: return TemplateArgumentDependence::None; case Expression: @@ -165,6 +205,7 @@ bool TemplateArgument::isPackExpansion() const { case Null: case Declaration: case Integral: + case UncommonValue: case Pack: case Template: case NullPtr: @@ -215,6 +256,9 @@ QualType TemplateArgument::getNonTypeTemplateArgumentType() const { case TemplateArgument::NullPtr: return getNullPtrType(); + + case TemplateArgument::UncommonValue: + return getUncommonValueType(); } llvm_unreachable("Invalid TemplateArgument Kind!"); @@ -259,8 +303,13 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, } case Integral: - getAsIntegral().Profile(ID); getIntegralType().Profile(ID); + getAsIntegral().Profile(ID); + break; + + case UncommonValue: + getUncommonValueType().Profile(ID); + getAsUncommonValue().Profile(ID); break; case Expression: @@ -296,6 +345,16 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { return getIntegralType() == Other.getIntegralType() && getAsIntegral() == Other.getAsIntegral(); + case UncommonValue: { + if (getUncommonValueType() != Other.getUncommonValueType()) + return false; + + llvm::FoldingSetNodeID A, B; + getAsUncommonValue().Profile(A); + Other.getAsUncommonValue().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) @@ -322,6 +381,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const { case Declaration: case Integral: + case UncommonValue: case Pack: case Null: case Template: @@ -361,6 +421,10 @@ void TemplateArgument::print(const PrintingPolicy &Policy, break; } + case UncommonValue: + getAsUncommonValue().printPretty(Out, Policy, getUncommonValueType()); + break; + case NullPtr: Out << "nullptr"; break; @@ -443,6 +507,9 @@ SourceRange TemplateArgumentLoc::getSourceRange() const { case TemplateArgument::Integral: return getSourceIntegralExpression()->getSourceRange(); + case TemplateArgument::UncommonValue: + return getSourceUncommonValueExpression()->getSourceRange(); + case TemplateArgument::Pack: case TemplateArgument::Null: return SourceRange(); @@ -471,6 +538,18 @@ static const T &DiagTemplateArg(const T &DB, const TemplateArgument &Arg) { case TemplateArgument::Integral: return DB << Arg.getAsIntegral().toString(10); + case TemplateArgument::UncommonValue: { + // FIXME: We're guessing at LangOptions! + SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + LangOptions LangOpts; + LangOpts.CPlusPlus = true; + PrintingPolicy Policy(LangOpts); + Arg.getAsUncommonValue().printPretty(OS, Policy, + Arg.getUncommonValueType()); + 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 222b1abac510..438b6950890b 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -562,6 +562,7 @@ void TemplateSpecializationTypeLoc::initializeArgLocs(ASTContext &Context, case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::UncommonValue: ArgInfos[i] = TemplateArgumentLocInfo(); break; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 00606d3ae507..46c49e80aae0 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1949,6 +1949,14 @@ CGDebugInfo::CollectTemplateParams(const TemplateParameterList *TPList, TemplateParams.push_back(DBuilder.createTemplateValueParameter( TheCU, Name, TTy, defaultParameter, V)); } break; + case TemplateArgument::UncommonValue: { + QualType T = TA.getUncommonValueType(); + llvm::DIType *TTy = getOrCreateType(T, Unit); + llvm::Constant *V = ConstantEmitter(CGM).emitAbstract( + SourceLocation(), TA.getAsUncommonValue(), T); + TemplateParams.push_back(DBuilder.createTemplateValueParameter( + TheCU, Name, TTy, defaultParameter, V)); + } break; case TemplateArgument::Template: TemplateParams.push_back(DBuilder.createTemplateTemplateParameter( TheCU, Name, nullptr, diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index ca1d3a937fa8..840541a4af20 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1366,11 +1366,11 @@ llvm::Constant *ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *CE) { if (!CE->hasAPValueResult()) return nullptr; const Expr *Inner = CE->getSubExpr()->IgnoreImplicit(); - QualType RetType; - if (auto *Call = dyn_cast<CallExpr>(Inner)) - RetType = Call->getCallReturnType(CGF->getContext()); - else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Inner)) - RetType = Ctor->getType(); + QualType RetType = Inner->getType(); + if (Inner->isLValue()) + RetType = CGF->getContext().getLValueReferenceType(RetType); + else if (Inner->isXValue()) + RetType = CGF->getContext().getRValueReferenceType(RetType); llvm::Constant *Res = emitAbstract(CE->getBeginLoc(), CE->getAPValueResult(), RetType); return Res; diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp index abaeb1a4232f..9ada9ac8c209 100644 --- a/clang/lib/Index/USRGeneration.cpp +++ b/clang/lib/Index/USRGeneration.cpp @@ -983,6 +983,10 @@ void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) { VisitType(Arg.getIntegralType()); Out << Arg.getAsIntegral(); break; + + case TemplateArgument::UncommonValue: + // FIXME: Visit value. + break; } } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 29038ab9fe1c..b1181944c7fd 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -2657,6 +2657,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, case TemplateArgument::Integral: case TemplateArgument::Expression: case TemplateArgument::NullPtr: + case TemplateArgument::UncommonValue: // [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 ac52612ea3b0..5f4d5b9632e0 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -5774,7 +5774,9 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, if (Notes.empty()) { // It's a constant expression. - Expr *E = ConstantExpr::Create(S.Context, Result.get(), Value); + Expr *E = Result.get(); + if (!isa<ConstantExpr>(E)) + E = ConstantExpr::Create(S.Context, Result.get(), Value); if (ReturnPreNarrowingValue) Value = std::move(PreNarrowingValue); return E; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 9f314685faf0..34f5e2c4fb67 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4086,6 +4086,7 @@ static bool isTemplateArgumentTemplateParameter( case TemplateArgument::NullPtr: case TemplateArgument::Integral: case TemplateArgument::Declaration: + case TemplateArgument::UncommonValue: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: return false; @@ -5419,6 +5420,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::UncommonValue: case TemplateArgument::NullPtr: // We've already checked this template argument, so just copy // it to the list of converted arguments. @@ -5565,11 +5567,10 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, 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::UncommonValue: 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"); @@ -6936,37 +6937,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return ArgResult; } - // Convert the APValue to a TemplateArgument. - switch (Value.getKind()) { - case APValue::None: - assert(ParamType->isNullPtrType()); - Converted = TemplateArgument(CanonParamType, /*isNullPtr*/true); - break; - case APValue::Indeterminate: - llvm_unreachable("result of constant evaluation should be initialized"); - break; - case APValue::Int: - assert(ParamType->isIntegralOrEnumerationType()); - Converted = 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()); - Converted = VD ? TemplateArgument(VD, 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() || @@ -6982,8 +6955,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 && @@ -7001,29 +6973,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, "null reference should not be a constant expression"); assert((!VD || !ParamType->isNullPtrType()) && "non-null value of type nullptr_t?"); - Converted = VD ? TemplateArgument(VD, CanonParamType) - : TemplateArgument(CanonParamType, /*isNullPtr*/true); - break; } - case APValue::Struct: - case APValue::Union: - // Get or create the corresponding template parameter object. - Converted = TemplateArgument( - Context.getTemplateParamObjectDecl(CanonParamType, Value), - 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; - } + Converted = TemplateArgument(Context, CanonParamType, Value); return ArgResult.get(); } @@ -7561,12 +7516,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 @@ -7582,7 +7534,7 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, CharacterLiteral::CharacterKind Kind; if (T->isWideCharType()) Kind = CharacterLiteral::Wide; - else if (T->isChar8Type() && getLangOpts().Char8) + else if (T->isChar8Type() && S.getLangOpts().Char8) Kind = CharacterLiteral::UTF8; else if (T->isChar16Type()) Kind = CharacterLiteral::UTF16; @@ -7591,29 +7543,133 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, else Kind = CharacterLiteral::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 = new (Context) CXXBoolLiteralExpr(Arg.getAsIntegral().getBoolValue(), - T, Loc); - } else if (T->isNullPtrType()) { - E = new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc); + E = new (S.Context) CXXBoolLiteralExpr(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_RValue, CK_IntegralCast, E, - nullptr, CurFPFeatureOverrides(), - Context.getTrivialTypeSourceInfo(OrigT, Loc), + E = CStyleCastExpr::Create(S.Context, OrigT, VK_RValue, 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: + // FIXME: Are these values possible? + 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_RValue; + 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::UncommonValue: + return BuildExpressionFromNonTypeTemplateArgumentValue( + *this, Arg.getUncommonValueType(), Arg.getAsUncommonValue(), Loc); + } + llvm_unreachable("Unhandled TemplateArgument::ArgKind enum"); +} + /// Match two template parameters within template parameter lists. static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old, bool Complain, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index ee4316e7a632..92895e0b3db1 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -276,6 +276,16 @@ checkDeducedTemplateArguments(ASTContext &Context, // All other combinations are incompatible. return DeducedTemplateArgument(); + case TemplateArgument::UncommonValue: + // If we deduced a value and a dependent expression, keep the value. + if (Y.getKind() == TemplateArgument::Expression || + (Y.getKind() == TemplateArgument::UncommonValue && + 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())) @@ -2361,20 +2371,18 @@ DeduceTemplateArguments(Sema &S, return Sema::TDK_NonDeducedMismatch; case TemplateArgument::Integral: - if (Arg.getKind() == TemplateArgument::Integral) { - if (hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral())) - return Sema::TDK_Success; + if (Arg.getKind() == TemplateArgument::Integral && + hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral())) + return Sema::TDK_Success; - Info.FirstArg = Param; - Info.SecondArg = Arg; - return Sema::TDK_NonDeducedMismatch; - } + Info.FirstArg = Param; + Info.SecondArg = Arg; + return Sema::TDK_NonDeducedMismatch; - if (Arg.getKind() == TemplateArgument::Expression) { - Info.FirstArg = Param; - Info.SecondArg = Arg; - return Sema::TDK_NonDeducedMismatch; - } + case TemplateArgument::UncommonValue: + if (Arg.getKind() == TemplateArgument::UncommonValue && + Arg.structurallyEquals(Param)) + return Sema::TDK_Success; Info.FirstArg = Param; Info.SecondArg = Arg; @@ -2383,28 +2391,34 @@ DeduceTemplateArguments(Sema &S, case TemplateArgument::Expression: if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, Param.getAsExpr())) { - if (Arg.getKind() == TemplateArgument::Integral) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - Arg.getAsIntegral(), - Arg.getIntegralType(), - /*ArrayBound=*/false, - Info, Deduced); - if (Arg.getKind() == TemplateArgument::NullPtr) - return DeduceNullPtrTemplateArgument(S, TemplateParams, NTTP, - Arg.getNullPtrType(), - Info, Deduced); - if (Arg.getKind() == TemplateArgument::Expression) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - Arg.getAsExpr(), Info, Deduced); - if (Arg.getKind() == TemplateArgument::Declaration) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - Arg.getAsDecl(), - Arg.getParamTypeForDecl(), - Info, Deduced); + switch (Arg.getKind()) { + case TemplateArgument::Integral: + case TemplateArgument::Expression: + case TemplateArgument::UncommonValue: + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, DeducedTemplateArgument(Arg), + Arg.getNonTypeTemplateArgumentType(), Info, Deduced); - Info.FirstArg = Param; - Info.SecondArg = Arg; - return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::NullPtr: + return DeduceNullPtrTemplateArgument( + S, TemplateParams, NTTP, Arg.getNullPtrType(), Info, Deduced); + + + case TemplateArgument::Declaration: + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, Arg.getAsDecl(), Arg.getParamTypeForDecl(), + Info, Deduced); + + case TemplateArgument::Null: + case TemplateArgument::Type: + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + case TemplateArgument::Pack: + Info.FirstArg = Param; + Info.SecondArg = Arg; + return Sema::TDK_NonDeducedMismatch; + } + llvm_unreachable("Unknown template argument kind"); } // Can't deduce anything, but that's okay. @@ -2592,6 +2606,9 @@ static bool isSameTemplateArg(ASTContext &Context, case TemplateArgument::Integral: return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()); + case TemplateArgument::UncommonValue: + return X.structurallyEquals(Y); + case TemplateArgument::Expression: { llvm::FoldingSetNodeID XID, YID; X.getAsExpr()->Profile(XID, Context, true); @@ -2657,9 +2674,9 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, E); } - case TemplateArgument::Integral: { - Expr *E = - BuildExpressionFromIntegralTemplateArgument(Arg, Loc).getAs<Expr>(); + case TemplateArgument::Integral: + case TemplateArgument::UncommonValue: { + Expr *E = BuildExpressionFromNonTypeTemplateArgument(Arg, Loc).get(); return TemplateArgumentLoc(TemplateArgument(E), E); } @@ -6101,11 +6118,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::UncommonValue: break; case TemplateArgument::Type: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 49ff7cd33fab..cb74f08830c8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1556,16 +1556,18 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( 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 1951aec3d17d..3c6365a075f4 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -1103,6 +1103,7 @@ Sema::getTemplateArgumentPackExpansionPattern( case TemplateArgument::NullPtr: case TemplateArgument::Template: case TemplateArgument::Integral: + case TemplateArgument::UncommonValue: case TemplateArgument::Pack: case TemplateArgument::Null: return TemplateArgumentLoc(); @@ -1153,6 +1154,7 @@ Optional<unsigned> Sema::getFullyPackExpandedSize(TemplateArgument Arg) { case TemplateArgument::NullPtr: case TemplateArgument::TemplateExpansion: case TemplateArgument::Integral: + case TemplateArgument::UncommonValue: case TemplateArgument::Pack: case TemplateArgument::Null: return None; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 0a596e50658b..0660c824b3cc 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3557,6 +3557,7 @@ class TreeTransform { case TemplateArgument::Null: case TemplateArgument::Integral: case TemplateArgument::Declaration: + case TemplateArgument::UncommonValue: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: case TemplateArgument::NullPtr: @@ -4229,7 +4230,8 @@ bool TreeTransform<Derived>::TransformTemplateArgument( case TemplateArgument::Integral: case TemplateArgument::NullPtr: - case TemplateArgument::Declaration: { + case TemplateArgument::Declaration: + case TemplateArgument::UncommonValue: { // 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. @@ -4256,9 +4258,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::UncommonValue) + Output = TemplateArgumentLoc( + TemplateArgument(getSema().Context, NewT, Arg.getAsUncommonValue()), + 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 d01e7c69d181..5c7187fd773d 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7100,6 +7100,7 @@ ASTRecordReader::readTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind) { case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::UncommonValue: case TemplateArgument::Pack: // FIXME: Is this right? return TemplateArgumentLocInfo(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 9c16ded13cd9..a5c62e3e6984 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5218,6 +5218,7 @@ void ASTRecordWriter::AddTemplateArgumentLocInfo( case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::UncommonValue: case TemplateArgument::Pack: // FIXME: Is this right? break; diff --git a/clang/test/CodeGenCXX/mangle-ms-templates.cpp b/clang/test/CodeGenCXX/mangle-ms-templates.cpp index 7402d367ae3e..c9149a473b6f 100644 --- a/clang/test/CodeGenCXX/mangle-ms-templates.cpp +++ b/clang/test/CodeGenCXX/mangle-ms-templates.cpp @@ -2,6 +2,7 @@ // 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 template<typename T> class Class { @@ -327,3 +328,12 @@ 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>) {} +#endif diff --git a/clang/test/CodeGenCXX/mangle-template.cpp b/clang/test/CodeGenCXX/mangle-template.cpp index 40688de7e12e..8326bf658f06 100644 --- a/clang/test/CodeGenCXX/mangle-template.cpp +++ b/clang/test/CodeGenCXX/mangle-template.cpp @@ -226,6 +226,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( @@ -245,7 +255,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>) {} @@ -255,6 +282,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 000000000000..289094be7f2b --- /dev/null +++ b/clang/test/CodeGenCXX/template-arguments.cpp @@ -0,0 +1,81 @@ +// 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 1) +// CONST: @_Z5valueILi1EE = weak_odr {{.*}} i32 1, +template int value<1>; + +// CHECK: call {{.*}} @_Z2idIyET_S0_(i64 -1) +// CONST: @_Z5valueILy18446744073709551615EE = weak_odr {{.*}} i64 -1, +template unsigned long long value<-1ULL>; + +// CHECK: call {{.*}} @_Z2idIfET_S0_(float 1.000000e+00) +// CONST: @_Z5valueILf3f800000EE = weak_odr {{.*}} float 1.000000e+00, +template float value<1.0f>; +// CHECK: call {{.*}} @_Z2idIdET_S0_(double 1.000000e+00) +// CONST: @_Z5valueILd3ff0000000000000EE = weak_odr {{.*}} double 1.000000e+00, +template double value<1.0>; + +int n; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* @n) +// CONST: @_Z5valueIXadL_Z1nEEE = weak_odr {{.*}} i32* @n, +template int *value<&n>; + +struct A { int a[3]; } a; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* getelementptr inbounds (%struct.A, %struct.A* @a, i32 0, i32 0, i32 0)) +// CONST: @_Z5valueIXadsoiL_Z1aEEEE = weak_odr {{.*}} i32* getelementptr inbounds (%struct.A, %struct.A* @a, i32 0, i32 0, i32 0), +template int *value<&a.a[0]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 4) to i32*)) +// CONST: @_Z5valueIXadsoiL_Z1aE4EEE = weak_odr {{.*}} i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 4) to i32*), +template int *value<&a.a[1]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 8) to i32*)) +// CONST: @_Z5valueIXadsoiL_Z1aE8EEE = weak_odr {{.*}} i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 8) to i32*), +template int *value<&a.a[2]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 12) to i32*)) +// CONST: @_Z5valueIXadsoiL_Z1aE12pEEE = weak_odr {{.*}} i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 12) to i32*), +template int *value<&a.a[3]>; + +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, i32* +// CHECK: store i32 2, i32* +// CHECK: bitcast { i32, i32 }* %{{.*}} to i64* +// CHECK: load i64, +// CHECK: call {{.*}} @_Z2idICiET_S1_(i64 % +// CONST: @_Z5valueIXtlCiLi1ELi2EEEE = weak_odr {{.*}} { i32, i32 } { i32 1, i32 2 }, +template _Complex int value<1 + 2j>; + +// CHECK: store float 1.000000e+00, float* +// CHECK: store float 2.000000e+00, float* +// CHECK: bitcast { float, float }* %{{.*}} to <2 x float>* +// CHECK: load <2 x float>, +// CHECK: call {{.*}} @_Z2idICfET_S1_(<2 x float> % +// 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> <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> <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}>; diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx17.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx17.cpp index 52cf51719f05..bc8a22e89041 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx17.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx17.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 d514465f7d67..48101cccfce0 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,24 @@ 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 IP2 = IntPtr<&s.n[2]>; +using IP2 = IntPtr<s.n + 2>; -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 IP3 = IntPtr<&s.n[3]>; +using IP3 = IntPtr<s.n + 3>; 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 +48,12 @@ using U1 = Union<U{.b = 1}>; // expected-error {{ diff erent 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}} @@ -307,11 +307,11 @@ namespace dependent { if constexpr (N < 10) return R<n>(); else if constexpr (N < 20) - return R<vn.v>(); // FIXME: expected-error 2{{refers to subobject}} + return R<vn.v>(); else if constexpr (N < 30) return S<&n>(); else if constexpr (N < 40) - return S<&vn.v>(); // FIXME: expected-error 2{{refers to subobject}} + return S<&vn.v>(); else if constexpr (N < 50) return T<V<int&>{n}>(); else if constexpr (N < 60) @@ -322,15 +322,15 @@ namespace dependent { return T<V<int*>{&vn.v}>(); } template<int Base> void check() { - auto v = f<Base + 0>(); // FIXME: expected-note 2{{instantiation of}} - auto w = f<Base + 1>(); // FIXME: expected-note 2{{instantiation of}} + auto v = f<Base + 0>(); + auto w = f<Base + 1>(); static_assert(!__is_same(decltype(v), decltype(w))); static_assert(v != w); } template void check<0>(); - template void check<10>(); // FIXME: expected-note 2{{instantiation of}} + template void check<10>(); template void check<20>(); - template void check<30>(); // FIXME: expected-note 2{{instantiation of}} + template void check<30>(); template void check<40>(); template void check<50>(); template void check<60>(); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index f1008319ddc7..8a4d3cbcf939 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1493,6 +1493,11 @@ bool CursorVisitor::VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL) { return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); return false; + case TemplateArgument::UncommonValue: + if (Expr *E = TAL.getSourceUncommonValueExpression()) + 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 180cf1858d04..851b418b6d7b 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -1375,6 +1375,9 @@ enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C, return CXTemplateArgumentKind_NullPtr; case TemplateArgument::Integral: return CXTemplateArgumentKind_Integral; + case TemplateArgument::UncommonValue: + // FIXME: Expose these values. + return CXTemplateArgumentKind_Invalid; case TemplateArgument::Template: return CXTemplateArgumentKind_Template; case TemplateArgument::TemplateExpansion: diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 2679ee52136d..ff4e15e7e070 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -816,6 +816,7 @@ enum TemplateArgumentKind { eTemplateArgumentKindExpression, eTemplateArgumentKindPack, eTemplateArgumentKindNullPtr, + eTemplateArgumentKindUncommonValue, }; /// Options that can be set for a formatter to alter its behavior. Not diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index c15b15e736fb..4f55cf7cfa79 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -7002,6 +7002,9 @@ TypeSystemClang::GetTemplateArgumentKind(lldb::opaque_compiler_type_t type, case clang::TemplateArgument::Pack: return eTemplateArgumentKindPack; + + case clang::TemplateArgument::UncommonValue: + return eTemplateArgumentKindUncommonValue; } 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