Author: Richard Smith Date: 2019-12-05T14:32:36-08:00 New Revision: b220662a45c8067a2ae485ae34c1138d93506df9
URL: https://github.com/llvm/llvm-project/commit/b220662a45c8067a2ae485ae34c1138d93506df9 DIFF: https://github.com/llvm/llvm-project/commit/b220662a45c8067a2ae485ae34c1138d93506df9.diff LOG: Properly convert all declaration non-type template arguments when forming non-type template parameter values. This reverts commit 93cc9dddd82f9e971f382ade6acf6634c5914966, which reverted commit 11d10527852b4d3ed738aa90d8bec0f398160593. We now always form `&x` when forming a pointer to a function rather than trying to use function-to-pointer decay. This matches the behavior of the old code in this case, but not the intent as described by the comments. Added: Modified: clang/lib/Sema/SemaTemplate.cpp clang/test/SemaCXX/exceptions-seh.cpp clang/test/SemaCXX/warn-bool-conversion.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index e800f7fe7424..7dd1e9075c10 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6968,100 +6968,73 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, ValueDecl *VD = Arg.getAsDecl(); - if (VD->getDeclContext()->isRecord() && - (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) || - isa<IndirectFieldDecl>(VD))) { - // If the value is a class member, we might have a pointer-to-member. - // Determine whether the non-type template template parameter is of - // pointer-to-member type. If so, we need to build an appropriate - // expression for a pointer-to-member, since a "normal" DeclRefExpr - // would refer to the member itself. - if (ParamType->isMemberPointerType()) { - QualType ClassType - = Context.getTypeDeclType(cast<RecordDecl>(VD->getDeclContext())); - NestedNameSpecifier *Qualifier - = NestedNameSpecifier::Create(Context, nullptr, false, - ClassType.getTypePtr()); - CXXScopeSpec SS; - SS.MakeTrivial(Context, Qualifier, Loc); - - // The actual value-ness of this is unimportant, but for - // internal consistency's sake, references to instance methods - // are r-values. - ExprValueKind VK = VK_LValue; - if (isa<CXXMethodDecl>(VD) && cast<CXXMethodDecl>(VD)->isInstance()) - VK = VK_RValue; - - ExprResult RefExpr = BuildDeclRefExpr(VD, - VD->getType().getNonReferenceType(), - VK, - Loc, - &SS); - if (RefExpr.isInvalid()) - return ExprError(); - - RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); - - // We might need to perform a trailing qualification conversion, since - // the element type on the parameter could be more qualified than the - // element type in the expression we constructed, and likewise for a - // function conversion. - bool ObjCLifetimeConversion; - QualType Ignored; - if (IsFunctionConversion(RefExpr.get()->getType(), ParamType, Ignored) || - IsQualificationConversion(RefExpr.get()->getType(), - ParamType.getUnqualifiedType(), false, - ObjCLifetimeConversion)) - RefExpr = ImpCastExprToType(RefExpr.get(), - ParamType.getUnqualifiedType(), CK_NoOp); - - // FIXME: We need to perform derived-to-base or base-to-derived - // pointer-to-member conversions here too. - assert(!RefExpr.isInvalid() && - Context.hasSameType(RefExpr.get()->getType(), - ParamType.getUnqualifiedType())); - return RefExpr; - } - } - - QualType T = VD->getType().getNonReferenceType(); + CXXScopeSpec SS; + if (ParamType->isMemberPointerType()) { + // If this is a pointer to member, we need to use a qualified name to + // form a suitable pointer-to-member constant. + assert(VD->getDeclContext()->isRecord() && + (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) || + isa<IndirectFieldDecl>(VD))); + QualType ClassType + = Context.getTypeDeclType(cast<RecordDecl>(VD->getDeclContext())); + NestedNameSpecifier *Qualifier + = NestedNameSpecifier::Create(Context, nullptr, false, + ClassType.getTypePtr()); + SS.MakeTrivial(Context, Qualifier, Loc); + } + + ExprResult RefExpr = BuildDeclarationNameExpr( + SS, DeclarationNameInfo(VD->getDeclName(), Loc), VD); + if (RefExpr.isInvalid()) + return ExprError(); - if (ParamType->isPointerType()) { - // When the non-type template parameter is a pointer, take the - // address of the declaration. - ExprResult RefExpr = BuildDeclRefExpr(VD, T, VK_LValue, Loc); + // For a pointer, the argument declaration is the pointee. Take its address. + QualType ElemT(RefExpr.get()->getType()->getArrayElementTypeNoTypeQual(), 0); + if (ParamType->isPointerType() && !ElemT.isNull() && + Context.hasSimilarType(ElemT, ParamType->getPointeeType())) { + // Decay an array argument if we want a pointer to its first element. + RefExpr = DefaultFunctionArrayConversion(RefExpr.get()); if (RefExpr.isInvalid()) return ExprError(); - - if (!Context.hasSameUnqualifiedType(ParamType->getPointeeType(), T) && - (T->isFunctionType() || T->isArrayType())) { - // Decay functions and arrays unless we're forming a pointer to array. - RefExpr = DefaultFunctionArrayConversion(RefExpr.get()); - if (RefExpr.isInvalid()) - return ExprError(); - - return RefExpr; + } else if (ParamType->isPointerType() || ParamType->isMemberPointerType()) { + // For any other pointer, take the address (or form a pointer-to-member). + RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); + if (RefExpr.isInvalid()) + return ExprError(); + } else { + assert(ParamType->isReferenceType() && + "unexpected type for decl template argument"); + } + + // At this point we should have the right value category. + assert(ParamType->isReferenceType() == RefExpr.get()->isLValue() && + "value kind mismatch for non-type template argument"); + + // The type of the template parameter can diff er from the type of the + // argument in various ways; convert it now if necessary. + QualType DestExprType = ParamType.getNonLValueExprType(Context); + if (!Context.hasSameType(RefExpr.get()->getType(), DestExprType)) { + CastKind CK; + QualType Ignored; + if (Context.hasSimilarType(RefExpr.get()->getType(), DestExprType) || + IsFunctionConversion(RefExpr.get()->getType(), DestExprType, Ignored)) { + CK = CK_NoOp; + } else if (ParamType->isVoidPointerType() && + RefExpr.get()->getType()->isPointerType()) { + CK = CK_BitCast; + } else { + // FIXME: Pointers to members can need conversion derived-to-base or + // base-to-derived conversions. We currently don't retain enough + // information to convert properly (we need to track a cast path or + // subobject number in the template argument). + llvm_unreachable( + "unexpected conversion required for non-type template argument"); } - - // Take the address of everything else - return CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); - } - - ExprValueKind VK = VK_RValue; - - // If the non-type template parameter has reference type, qualify the - // resulting declaration reference with the extra qualifiers on the - // type that the reference refers to. - if (const ReferenceType *TargetRef = ParamType->getAs<ReferenceType>()) { - VK = VK_LValue; - T = Context.getQualifiedType(T, - TargetRef->getPointeeType().getQualifiers()); - } else if (isa<FunctionDecl>(VD)) { - // References to functions are always lvalues. - VK = VK_LValue; + RefExpr = ImpCastExprToType(RefExpr.get(), DestExprType, CK, + RefExpr.get()->getValueKind()); } - return BuildDeclRefExpr(VD, T, VK, Loc); + return RefExpr; } /// Construct a new expression that refers to the given diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp index 1d8cc4917e98..02bb786160dc 100644 --- a/clang/test/SemaCXX/exceptions-seh.cpp +++ b/clang/test/SemaCXX/exceptions-seh.cpp @@ -39,14 +39,13 @@ void instantiate_bad_scope_tmpl() { } #if __cplusplus < 201103L -// FIXME: Diagnose this case. For now we produce undef in codegen. template <typename T, T FN()> T func_template() { - return FN(); + return FN(); // expected-error 2{{builtin functions must be directly called}} } void inject_builtins() { - func_template<void *, __exception_info>(); - func_template<unsigned long, __exception_code>(); + func_template<void *, __exception_info>(); // expected-note {{instantiation of}} + func_template<unsigned long, __exception_code>(); // expected-note {{instantiation of}} } #endif diff --git a/clang/test/SemaCXX/warn-bool-conversion.cpp b/clang/test/SemaCXX/warn-bool-conversion.cpp index ab563aa68988..6eca171f254e 100644 --- a/clang/test/SemaCXX/warn-bool-conversion.cpp +++ b/clang/test/SemaCXX/warn-bool-conversion.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,expected-cxx11 %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify=expected,expected-cxx11 -std=c++11 %s namespace BooleanFalse { int* j = false; @@ -192,3 +192,23 @@ namespace macros { // expected-warning@-1{{address of 'x' will always evaluate to 'true'}} } } + +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}} + template<int (*p)[3]> void g() { if (p) {} } // expected-warning 2{{will always evaluate to 'true'}} expected-cxx11-warning {{implicit conversion of nullptr}} + template<int (*p)()> void h() { if (p) {} } + + int a, b[3], c[3][3], d(); + template void f<&a>(); // expected-note {{instantiation of}} + template void f<b>(); // expected-note {{instantiation of}} +#if __cplusplus >= 201103L + template void f<(int*)nullptr>(); // expected-note {{instantiation of}} +#endif + template void g<&b>(); // expected-note {{instantiation of}} + template void g<c>(); // expected-note {{instantiation of}} +#if __cplusplus >= 201103L + template void g<(int(*)[3])nullptr>(); // expected-note {{instantiation of}} +#endif + template void h<d>(); +} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index 7a58dd5dcaed..7232598215ac 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -394,6 +394,21 @@ namespace PR42362 { Z<f, f, f>::Q q; } +namespace QualConv { + int *X; + template<const int *const *P> void f() { + using T = decltype(P); + using T = const int* const*; + } + template void f<&X>(); + + template<const int *const &R> void g() { + using T = decltype(R); + using T = const int *const &; + } + template void g<(const int *const&)X>(); +} + namespace FunctionConversion { struct a { void c(char *) noexcept; }; template<void (a::*f)(char*)> void g() { @@ -401,4 +416,21 @@ namespace FunctionConversion { using T = void (a::*)(char*); // (not 'noexcept') } template void g<&a::c>(); + + void c() noexcept; + template<void (*p)()> void h() { + using T = decltype(p); + using T = void (*)(); // (not 'noexcept') + } + template void h<&c>(); +} + +namespace VoidPtr { + // Note, this is an extension in C++17 but valid in C++20. + template<void *P> void f() { + using T = decltype(P); + using T = void*; + } + int n; + template void f<(void*)&n>(); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits