[cfe-commits] r64317 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.def lib/Sema/Sema.h lib/Sema/SemaTemplate.cpp test/SemaTemplate/temp_arg_nontype.cpp

Douglas Gregor dgregor at apple.com
Wed Feb 11 11:52:56 PST 2009


Author: dgregor
Date: Wed Feb 11 13:52:55 2009
New Revision: 64317

URL: http://llvm.org/viewvc/llvm-project?rev=64317&view=rev
Log:
Finished semantic analysis of non-type template arguments, to check
for non-external names whose address becomes the template
argument. This completes C++ [temp.arg.nontype]p1.

Note that our interpretation of C++ [temp.arg.nontype]p1b3 differs
from EDG's interpretation (we're stricter, and GCC agrees with
us). They're opening a core issue about the matter.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def?rev=64317&r1=64316&r2=64317&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def Wed Feb 11 13:52:55 2009
@@ -523,7 +523,7 @@
      "unnamed type used in template argument was declared here")
 DIAG(err_template_arg_not_class_template, ERROR,
      "template argument does not refer to a class template")
-DIAG(note_template_arg_refers_here, NOTE,
+DIAG(note_template_arg_refers_here_func, NOTE,
      "template argument refers to function template %0, here")
 DIAG(err_template_arg_template_params_mismatch, ERROR,
      "template template argument has different template parameters than its corresponding template template parameter")
@@ -537,7 +537,26 @@
      "non-type template parameter of reference type %0 cannot bind to template argument of type %1") 
 DIAG(err_template_arg_ref_bind_ignores_quals, ERROR,
      "reference binding of non-type template parameter of type %0 to template argument of type %1 ignores qualifiers")
-
+DIAG(err_template_arg_not_object_or_func_form, ERROR,
+     "non-type template argument does not directly refer to an object or function")
+DIAG(err_template_arg_field, ERROR,
+     "non-type template argument refers to non-static data member %0")
+DIAG(err_template_arg_method, ERROR,
+     "non-type template argument refers to non-static member function %0")
+DIAG(err_template_arg_function_not_extern, ERROR,
+     "non-template argument refers to function %0 with internal linkage")
+DIAG(err_template_arg_object_not_extern, ERROR,
+     "non-template argument refers to object %0 that does not have external linkage")
+DIAG(note_template_arg_internal_object, NOTE,
+     "non-template argument refers to %select{function|object}0 here")
+DIAG(note_template_arg_refers_here, NOTE,
+     "non-template argument refers here")
+DIAG(err_template_arg_not_object_or_func, ERROR,
+     "non-type template argument does not refer to an object or function")
+DIAG(err_template_arg_not_pointer_to_member_form, ERROR,
+     "non-type template argument is not a pointer to member constant")
+DIAG(err_template_arg_extra_parens, ERROR,
+     "non-type template argument cannot be surrounded by parentheses")
 
 DIAG(err_unexpected_typedef, ERROR,
      "unexpected type name %0: expected expression")

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=64317&r1=64316&r2=64317&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Feb 11 13:52:55 2009
@@ -1547,6 +1547,8 @@
 
   bool CheckTemplateArgument(TemplateTypeParmDecl *Param, QualType Arg,
                              SourceLocation ArgLoc);
+  bool CheckTemplateArgumentAddressOfObjectOrFunction(Expr *Arg);
+  bool CheckTemplateArgumentPointerToMember(Expr *Arg);
   bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Expr *&Arg);
   bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg);
   bool TemplateParameterListsAreEqual(TemplateParameterList *New,

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=64317&r1=64316&r2=64317&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Feb 11 13:52:55 2009
@@ -14,6 +14,7 @@
 #include "Sema.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Parse/DeclSpec.h"
 #include "clang/Basic/LangOptions.h"
@@ -825,6 +826,158 @@
   return false;
 }
 
+/// \brief Checks whether the given template argument is the address
+/// of an object or function according to C++ [temp.arg.nontype]p1.
+bool Sema::CheckTemplateArgumentAddressOfObjectOrFunction(Expr *Arg) {
+  bool Invalid = false;
+
+  // See through any implicit casts we added to fix the type.
+  if (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(Arg))
+    Arg = Cast->getSubExpr();
+
+  // C++ [temp.arg.nontype]p1:
+  // 
+  //   A template-argument for a non-type, non-template
+  //   template-parameter shall be one of: [...]
+  //
+  //     -- the address of an object or function with external
+  //        linkage, including function templates and function
+  //        template-ids but excluding non-static class members,
+  //        expressed as & id-expression where the & is optional if
+  //        the name refers to a function or array, or if the
+  //        corresponding template-parameter is a reference; or
+  DeclRefExpr *DRE = 0;
+  
+  // Ignore (and complain about) any excess parentheses.
+  while (ParenExpr *Parens = dyn_cast<ParenExpr>(Arg)) {
+    if (!Invalid) {
+      Diag(Arg->getSourceRange().getBegin(), 
+           diag::err_template_arg_extra_parens)
+        << Arg->getSourceRange();
+      Invalid = true;
+    }
+
+    Arg = Parens->getSubExpr();
+  }
+
+  if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg)) {
+    if (UnOp->getOpcode() == UnaryOperator::AddrOf)
+      DRE = dyn_cast<DeclRefExpr>(UnOp->getSubExpr());
+  } else
+    DRE = dyn_cast<DeclRefExpr>(Arg);
+
+  if (!DRE || !isa<ValueDecl>(DRE->getDecl()))
+    return Diag(Arg->getSourceRange().getBegin(), 
+                diag::err_template_arg_not_object_or_func_form)
+      << Arg->getSourceRange();
+
+  // Cannot refer to non-static data members
+  if (FieldDecl *Field = dyn_cast<FieldDecl>(DRE->getDecl()))
+    return Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_field)
+      << Field << Arg->getSourceRange();
+
+  // Cannot refer to non-static member functions
+  if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(DRE->getDecl()))
+    if (!Method->isStatic())
+      return Diag(Arg->getSourceRange().getBegin(), 
+                  diag::err_template_arg_method)
+        << Method << Arg->getSourceRange();
+   
+  // Functions must have external linkage.
+  if (FunctionDecl *Func = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+    if (Func->getStorageClass() == FunctionDecl::Static) {
+      Diag(Arg->getSourceRange().getBegin(), 
+           diag::err_template_arg_function_not_extern)
+        << Func << Arg->getSourceRange();
+      Diag(Func->getLocation(), diag::note_template_arg_internal_object)
+        << true;
+      return true;
+    }
+
+    // Okay: we've named a function with external linkage.
+    return Invalid;
+  }
+
+  if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
+    if (!Var->hasGlobalStorage()) {
+      Diag(Arg->getSourceRange().getBegin(), 
+           diag::err_template_arg_object_not_extern)
+        << Var << Arg->getSourceRange();
+      Diag(Var->getLocation(), diag::note_template_arg_internal_object)
+        << true;
+      return true;
+    }
+
+    // Okay: we've named an object with external linkage
+    return Invalid;
+  }
+  
+  // We found something else, but we don't know specifically what it is.
+  Diag(Arg->getSourceRange().getBegin(), 
+       diag::err_template_arg_not_object_or_func)
+      << Arg->getSourceRange();
+  Diag(DRE->getDecl()->getLocation(), 
+       diag::note_template_arg_refers_here);
+  return true;
+}
+
+/// \brief Checks whether the given template argument is a pointer to
+/// member constant according to C++ [temp.arg.nontype]p1.
+bool Sema::CheckTemplateArgumentPointerToMember(Expr *Arg) {
+  bool Invalid = false;
+
+  // See through any implicit casts we added to fix the type.
+  if (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(Arg))
+    Arg = Cast->getSubExpr();
+
+  // C++ [temp.arg.nontype]p1:
+  // 
+  //   A template-argument for a non-type, non-template
+  //   template-parameter shall be one of: [...]
+  //
+  //     -- a pointer to member expressed as described in 5.3.1.
+  QualifiedDeclRefExpr *DRE = 0;
+
+  // Ignore (and complain about) any excess parentheses.
+  while (ParenExpr *Parens = dyn_cast<ParenExpr>(Arg)) {
+    if (!Invalid) {
+      Diag(Arg->getSourceRange().getBegin(), 
+           diag::err_template_arg_extra_parens)
+        << Arg->getSourceRange();
+      Invalid = true;
+    }
+
+    Arg = Parens->getSubExpr();
+  }
+
+  if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg))
+    if (UnOp->getOpcode() == UnaryOperator::AddrOf)
+      DRE = dyn_cast<QualifiedDeclRefExpr>(UnOp->getSubExpr());
+
+  if (!DRE)
+    return Diag(Arg->getSourceRange().getBegin(),
+                diag::err_template_arg_not_pointer_to_member_form)
+      << Arg->getSourceRange();
+
+  if (isa<FieldDecl>(DRE->getDecl()) || isa<CXXMethodDecl>(DRE->getDecl())) {
+    assert((isa<FieldDecl>(DRE->getDecl()) ||
+            !cast<CXXMethodDecl>(DRE->getDecl())->isStatic()) &&
+           "Only non-static member pointers can make it here");
+
+    // Okay: this is the address of a non-static member, and therefore
+    // a member pointer constant.
+    return Invalid;
+  }
+
+  // We found something else, but we don't know specifically what it is.
+  Diag(Arg->getSourceRange().getBegin(), 
+       diag::err_template_arg_not_pointer_to_member_form)
+      << Arg->getSourceRange();
+  Diag(DRE->getDecl()->getLocation(), 
+       diag::note_template_arg_refers_here);
+  return true;
+}
+
 /// \brief Check a template argument against its corresponding
 /// non-type template parameter.
 ///
@@ -921,7 +1074,8 @@
       (ParamType->isMemberPointerType() &&
        ParamType->getAsMemberPointerType()->getPointeeType()
          ->isFunctionType())) {
-    if (Context.hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) {
+    if (Context.hasSameUnqualifiedType(ArgType, 
+                                       ParamType.getNonReferenceType())) {
       // We don't have to do anything: the types already match.
     } else if (ArgType->isFunctionType() && ParamType->isPointerType()) {
       ArgType = Context.getPointerType(ArgType);
@@ -936,7 +1090,8 @@
       }
     }
 
-    if (!Context.hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) {
+    if (!Context.hasSameUnqualifiedType(ArgType, 
+                                        ParamType.getNonReferenceType())) {
       // We can't perform this conversion.
       Diag(Arg->getSourceRange().getBegin(), 
            diag::err_template_arg_not_convertible)
@@ -944,9 +1099,11 @@
       Diag(Param->getLocation(), diag::note_template_param_here);
       return true;
     }
-
-    // FIXME: Check the restrictions in p1!
-    return false;
+    
+    if (ParamType->isMemberPointerType())
+      return CheckTemplateArgumentPointerToMember(Arg);
+    
+    return CheckTemplateArgumentAddressOfObjectOrFunction(Arg);
   }
 
   if (const PointerType *ParamPtrType = ParamType->getAsPointerType()) {
@@ -975,8 +1132,7 @@
       return true;
     }
     
-    // FIXME: Check the restrictions in p1!
-    return false;
+    return CheckTemplateArgumentAddressOfObjectOrFunction(Arg);
   }
     
   if (const ReferenceType *ParamRefType = ParamType->getAsReferenceType()) {
@@ -1011,10 +1167,7 @@
       return true;
     }
     
-    // FIXME: Check the restrictions in p1!
-    // CheckAddressConstantExpression(Lvalue) can be modified to do
-    // this.
-    return false;
+    return CheckTemplateArgumentAddressOfObjectOrFunction(Arg);
   }
 
   //     -- For a non-type template-parameter of type pointer to data
@@ -1034,9 +1187,7 @@
     return true;    
   }
 
-  // FIXME: Check the restrictions in p1.
-
-  return false;
+  return CheckTemplateArgumentPointerToMember(Arg);
 }
 
 /// \brief Check a template argument against its corresponding
@@ -1059,7 +1210,8 @@
   if (!isa<ClassTemplateDecl>(Template)) {
     assert(isa<FunctionTemplateDecl>(Template) && 
            "Only function templates are possible here");
-    Diag(Arg->getSourceRange().getBegin(), diag::note_template_arg_refers_here)
+    Diag(Arg->getSourceRange().getBegin(), 
+         diag::note_template_arg_refers_here_func)
       << Template;
   }
 

Modified: cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp?rev=64317&r1=64316&r2=64317&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_nontype.cpp Wed Feb 11 13:52:55 2009
@@ -45,9 +45,13 @@
 template<X const *Ptr> struct A2;
 
 X *X_ptr;
+X an_X;
 X array_of_Xs[10];
 A2<X_ptr> *a12;
 A2<array_of_Xs> *a13;
+A2<&an_X> *a13_2;
+A2<(&an_X)> *a13_2; // expected-error{{non-type template argument cannot be surrounded by parentheses}} \
+                    // FIXME: expected-error{{unqualified-id}}
 
 float f(float);
 
@@ -62,7 +66,6 @@
 A3<&h> *a14_2;
 A3<f> *a14_3;
 A3<&f> *a14_4;
-A3<((&f))> *a14_5;
 A3<h2> *a14_6;  // expected-error{{non-type template argument of type 'float (*)(float)' cannot be converted to a value of type 'int (*)(int)'}} \
 // FIXME: expected-error{{expected unqualified-id}}
 A3<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (*)(int)'}}\
@@ -75,7 +78,7 @@
 
 volatile X * X_volatile_ptr;
 template<X const &AnX> struct A4; // expected-note 2{{template parameter is declared here}}
-A4<*X_ptr> *a15_1; // okay
+A4<an_X> *a15_1; // okay
 A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type template parameter of type 'class X const &' to template argument of type 'class X volatile' ignores qualifiers}} \
                   // FIXME: expected-error{{expected unqualified-id}}
 A4<y> *15_3; //  expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}}\
@@ -83,9 +86,7 @@
 
 template<int (&fr)(int)> struct A5; // expected-note 2{{template parameter is declared here}}
 A5<h> *a16_1;
-A5<(h)> *a16_2;
 A5<f> *a16_3;
-A5<(f)> *a16_4;
 A5<h2> *a16_6;  // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}} \
 // FIXME: expected-error{{expected unqualified-id}}
 A5<g> *a14_7; // expected-error{{non-type template argument of type '<overloaded function type>' cannot be converted to a value of type 'int (&)(int)'}}\
@@ -115,3 +116,5 @@
 A7c<&Z::int_member> *a18_2;
 A7<&Z::float_member> *a18_3; // expected-error{{non-type template argument of type 'float struct Z::*' cannot be converted to a value of type 'int struct Z::*'}} \
               // FIXME: expected-error{{unqualified-id}}
+A7c<(&Z::int_member)> *a18_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}} \
+          // FIXME: expected-error{{expected unqualified-id}}





More information about the cfe-commits mailing list