diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-02-11 19:52:55 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-02-11 19:52:55 +0000 |
commit | cc45cb3630b42c5245e26593e385097c220bc859 (patch) | |
tree | 0a0096d47c4a1c5d147c73be6720ffa848cd138d | |
parent | 5fa77e9662e0cade8b4f491588c14c70c48e0987 (diff) |
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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64317 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.def | 23 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 182 | ||||
-rw-r--r-- | test/SemaTemplate/temp_arg_nontype.cpp | 11 |
4 files changed, 197 insertions, 21 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 25fe8254bb..34fc2e8ef7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -523,7 +523,7 @@ DIAG(note_template_unnamed_type_here, NOTE, "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 @@ DIAG(err_template_arg_no_ref_bind, ERROR, "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") diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index b11cc76cee..c236b89a5e 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1547,6 +1547,8 @@ public: 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, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 429dc8e1e3..1c2484d972 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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 @@ bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param, 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 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, (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 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, } } - 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 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, 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 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return true; } - // FIXME: Check the restrictions in p1! - return false; + return CheckTemplateArgumentAddressOfObjectOrFunction(Arg); } if (const ReferenceType *ParamRefType = ParamType->getAsReferenceType()) { @@ -1011,10 +1167,7 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, 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 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, 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 @@ bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, 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; } diff --git a/test/SemaTemplate/temp_arg_nontype.cpp b/test/SemaTemplate/temp_arg_nontype.cpp index 63cfa8fac3..89dbf237b3 100644 --- a/test/SemaTemplate/temp_arg_nontype.cpp +++ b/test/SemaTemplate/temp_arg_nontype.cpp @@ -45,9 +45,13 @@ A<X(17, 42)> *a11; // expected-error{{non-type template argument of type 'class 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_1; 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 @@ struct Y { } y; 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 @@ A4<y> *15_3; // expected-error{{non-type template parameter of reference type ' 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 @@ A7<&Z::int_member> *a18_1; 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}} |