[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