[clang] b220662 - Properly convert all declaration non-type template arguments when
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 5 14:32:54 PST 2019
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 at -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>();
}
More information about the cfe-commits
mailing list