summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-02-09 23:23:08 +0000
committerDouglas Gregor <dgregor@apple.com>2009-02-09 23:23:08 +0000
commitc15cb38a4ff717097b32532fbf761c71b1376a02 (patch)
tree7c232b8e658549facb629b726eb39951f20f1759
parent672c91db00d28187600dd18ef6c524ff45e95ef2 (diff)
downloadclang-c15cb38a4ff717097b32532fbf761c71b1376a02.tar.gz
Rudimentary checking of template arguments against their corresponding
template parameters when performing semantic analysis of a template-id naming a class template specialization. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64185 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.def22
-rw-r--r--include/clang/Parse/Action.h2
-rw-r--r--include/clang/Parse/Ownership.h2
-rw-r--r--include/clang/Parse/Parser.h4
-rw-r--r--lib/AST/Type.cpp2
-rw-r--r--lib/Parse/ParseTemplate.cpp23
-rw-r--r--lib/Sema/Sema.h16
-rw-r--r--lib/Sema/SemaExpr.cpp4
-rw-r--r--lib/Sema/SemaTemplate.cpp182
-rw-r--r--test/SemaTemplate/temp_arg.cpp13
-rw-r--r--test/SemaTemplate/temp_arg_type.cpp27
11 files changed, 285 insertions, 12 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def
index 539b659d63..aa56be8bc8 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.def
+++ b/include/clang/Basic/DiagnosticSemaKinds.def
@@ -464,7 +464,7 @@ DIAG(err_ovl_surrogate_cand, NOTE,
DIAG(err_member_call_without_object, ERROR,
"call to non-static member function without an object argument")
-/// C++ Templates Semantic Analysis
+// C++ Template Declarations
DIAG(err_template_param_shadow, ERROR,
"declaration of %0 shadows template parameter")
DIAG(note_template_param_here, NOTE,
@@ -488,6 +488,26 @@ DIAG(err_template_nontype_parm_different_type, ERROR,
DIAG(note_template_nontype_parm_prev_declaration, NOTE,
"previous non-type template parameter with type %0 is here")
+// C++ Template Argument Lists
+DIAG(err_template_arg_list_different_arity, ERROR,
+ "%select{too few|too many}0 template arguments for %select{class template|function template|template template parameter|template}1 %2")
+DIAG(note_template_parameter_here, ERROR,
+ "template parameter is declared here")
+DIAG(err_template_arg_must_be_type, ERROR,
+ "template argument for template type parameter must be a type")
+DIAG(err_template_arg_must_be_expr, ERROR,
+ "template argument for non-type template parameter must be an expression")
+DIAG(err_template_arg_nontype_ambig, ERROR,
+ "template argument for non-type template parameter is treated as type %0")
+DIAG(err_template_arg_must_be_template, ERROR,
+ "template argument for template template parameter must be a template")
+DIAG(err_template_arg_local_type, ERROR,
+ "template argument uses local type %0")
+DIAG(err_template_arg_unnamed_type, ERROR,
+ "template argument uses unnamed type")
+DIAG(note_template_unnamed_type_here, NOTE,
+ "unnamed type used in template argument was declared here")
+
DIAG(err_unexpected_typedef, ERROR,
"unexpected type name %0: expected expression")
DIAG(err_unexpected_namespace, ERROR,
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index 14b2cea81a..efa94a2dc7 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -1134,8 +1134,10 @@ public:
/// new class template specialization.
virtual TypeTy *
ActOnClassTemplateSpecialization(DeclTy *Template,
+ SourceLocation TemplateLoc,
SourceLocation LAngleLoc,
ASTTemplateArgsPtr TemplateArgs,
+ SourceLocation *TemplateArgLocs,
SourceLocation RAngleLoc,
const CXXScopeSpec *SS = 0) {
return 0;
diff --git a/include/clang/Parse/Ownership.h b/include/clang/Parse/Ownership.h
index f191d9a3b4..cae8815ab0 100644
--- a/include/clang/Parse/Ownership.h
+++ b/include/clang/Parse/Ownership.h
@@ -567,7 +567,7 @@ namespace clang
#endif
Args(Other.Args), ArgIsType(Other.ArgIsType), Count(Other.Count) {
#if !defined(DISABLE_SMART_POINTERS)
- Other.destroy();
+ Other.Count = 0;
#endif
}
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 64b2f134e8..2d777ff7ba 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1007,10 +1007,12 @@ private:
// C++ 14.3: Template arguments [temp.arg]
typedef llvm::SmallVector<void *, 16> TemplateArgList;
typedef llvm::SmallVector<bool, 16> TemplateArgIsTypeList;
+ typedef llvm::SmallVector<SourceLocation, 16> TemplateArgLocationList;
void AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
const CXXScopeSpec *SS = 0);
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
- TemplateArgIsTypeList &TemplateArgIsType);
+ TemplateArgIsTypeList &TemplateArgIsType,
+ TemplateArgLocationList &TemplateArgLocations);
void *ParseTemplateArgument(bool &ArgIsType);
//===--------------------------------------------------------------------===//
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 62875ed5cb..ac8e4f110e 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -943,7 +943,7 @@ bool ClassTemplateSpecializationType::isArgType(unsigned Arg) const {
const unsigned BitsPerWord = sizeof(uintptr_t) * CHAR_BIT;
const uintptr_t *Data = reinterpret_cast<const uintptr_t *>(this + 1);
Data += Arg / BitsPerWord;
- return (*Data >> (Arg % BitsPerWord)) & 0x01;
+ return (*Data >> ((NumArgs - Arg) % BitsPerWord - 1)) & 0x01;
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp
index fa76714139..f860058117 100644
--- a/lib/Parse/ParseTemplate.cpp
+++ b/lib/Parse/ParseTemplate.cpp
@@ -370,10 +370,13 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
// Parse the optional template-argument-list.
TemplateArgList TemplateArgs;
TemplateArgIsTypeList TemplateArgIsType;
+ TemplateArgLocationList TemplateArgLocations;
+
{
GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
if (Tok.isNot(tok::greater) &&
- ParseTemplateArgumentList(TemplateArgs, TemplateArgIsType)) {
+ ParseTemplateArgumentList(TemplateArgs, TemplateArgIsType,
+ TemplateArgLocations)) {
// Try to find the closing '>'.
SkipUntil(tok::greater, true, true);
@@ -417,9 +420,14 @@ void Parser::AnnotateTemplateIdToken(DeclTy *Template, TemplateNameKind TNK,
&TemplateArgIsType[0],
TemplateArgs.size());
TypeTy *Ty
- = Actions.ActOnClassTemplateSpecialization(Template, LAngleLoc,
- TemplateArgsPtr,
+ = Actions.ActOnClassTemplateSpecialization(Template, TemplateNameLoc,
+ LAngleLoc, TemplateArgsPtr,
+ &TemplateArgLocations[0],
RAngleLoc, SS);
+
+ if (!Ty) // Something went wrong; don't annotate
+ return;
+
Tok.setKind(tok::annot_typename);
Tok.setAnnotationValue(Ty);
}
@@ -453,8 +461,8 @@ void *Parser::ParseTemplateArgument(bool &ArgIsType) {
return ParseTypeName();
}
- OwningExprResult ExprArg = ParseExpression();
- if (ExprArg.isInvalid())
+ OwningExprResult ExprArg = ParseAssignmentExpression();
+ if (ExprArg.isInvalid() || !ExprArg.get())
return 0;
ArgIsType = false;
@@ -469,13 +477,16 @@ void *Parser::ParseTemplateArgument(bool &ArgIsType) {
/// template-argument-list ',' template-argument
bool
Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
- TemplateArgIsTypeList &TemplateArgIsType) {
+ TemplateArgIsTypeList &TemplateArgIsType,
+ TemplateArgLocationList &TemplateArgLocations) {
while (true) {
bool IsType = false;
+ SourceLocation Loc = Tok.getLocation();
void *Arg = ParseTemplateArgument(IsType);
if (Arg) {
TemplateArgs.push_back(Arg);
TemplateArgIsType.push_back(IsType);
+ TemplateArgLocations.push_back(Loc);
} else {
SkipUntil(tok::comma, tok::greater, true, true);
return true;
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index ca4c528d98..353f891391 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -62,6 +62,7 @@ namespace clang {
class TypedefDecl;
class TemplateDecl;
class TemplateParameterList;
+ class TemplateTemplateParmDecl;
class ObjCInterfaceDecl;
class ObjCCompatibleAliasDecl;
class ObjCProtocolDecl;
@@ -1515,12 +1516,25 @@ public:
MultiTemplateParamsArg TemplateParameterLists);
virtual TypeTy *
- ActOnClassTemplateSpecialization(DeclTy *Template,
+ ActOnClassTemplateSpecialization(DeclTy *Template,
+ SourceLocation TemplateLoc,
SourceLocation LAngleLoc,
ASTTemplateArgsPtr TemplateArgs,
+ SourceLocation *TemplateArgLocs,
SourceLocation RAngleLoc,
const CXXScopeSpec *SS = 0);
+ bool CheckTemplateArgumentList(TemplateDecl *Template,
+ SourceLocation TemplateLoc,
+ SourceLocation LAngleLoc,
+ ASTTemplateArgsPtr& TemplateArgs,
+ SourceLocation *TemplateArgLocs,
+ SourceLocation RAngleLoc);
+
+ bool CheckTemplateArgument(TemplateTypeParmDecl *Param, QualType Arg,
+ SourceLocation ArgLoc);
+ bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Expr *Arg);
+ bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg);
bool TemplateParameterListsAreEqual(TemplateParameterList *New,
TemplateParameterList *Old,
bool Complain,
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index af417963b1..c2051a5016 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -746,7 +746,9 @@ Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
if (OverloadedFunctionDecl *Ovl = dyn_cast<OverloadedFunctionDecl>(D))
return Owned(BuildDeclRefExpr(Ovl, Context.OverloadTy, Loc,
false, false, SS));
-
+ else if (TemplateDecl *Template = dyn_cast<TemplateDecl>(D))
+ return Owned(BuildDeclRefExpr(Template, Context.OverloadTy, Loc,
+ false, false, SS));
ValueDecl *VD = cast<ValueDecl>(D);
// check if referencing an identifier with __attribute__((deprecated)).
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 740198b11b..297410471b 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -356,14 +356,24 @@ Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
return NewTemplate;
}
+
+
Action::TypeTy *
Sema::ActOnClassTemplateSpecialization(DeclTy *TemplateD,
+ SourceLocation TemplateLoc,
SourceLocation LAngleLoc,
ASTTemplateArgsPtr TemplateArgs,
+ SourceLocation *TemplateArgLocs,
SourceLocation RAngleLoc,
const CXXScopeSpec *SS) {
TemplateDecl *Template = cast<TemplateDecl>(static_cast<Decl *>(TemplateD));
+ // Check that the template argument list is well-formed for this
+ // template.
+ if (!CheckTemplateArgumentList(Template, TemplateLoc, LAngleLoc,
+ TemplateArgs, TemplateArgLocs, RAngleLoc))
+ return 0;
+
// Yes, all class template specializations are just silly sugar for
// 'int'. Gotta problem wit dat?
QualType Result
@@ -376,6 +386,178 @@ Sema::ActOnClassTemplateSpecialization(DeclTy *TemplateD,
return Result.getAsOpaquePtr();
}
+/// \brief Check that the given template argument list is well-formed
+/// for specializing the given template.
+bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
+ SourceLocation TemplateLoc,
+ SourceLocation LAngleLoc,
+ ASTTemplateArgsPtr& Args,
+ SourceLocation *TemplateArgLocs,
+ SourceLocation RAngleLoc) {
+ TemplateParameterList *Params = Template->getTemplateParameters();
+ unsigned NumParams = Params->size();
+ unsigned NumArgs = Args.size();
+ bool Invalid = false;
+
+ if (NumArgs > NumParams ||
+ NumArgs < NumParams /*FIXME: default arguments! */) {
+ // FIXME: point at either the first arg beyond what we can handle,
+ // or the '>', depending on whether we have too many or too few
+ // arguments.
+ SourceRange Range;
+ if (NumArgs > NumParams)
+ Range = SourceRange(TemplateArgLocs[NumParams], RAngleLoc);
+ Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+ << (NumArgs > NumParams)
+ << (isa<ClassTemplateDecl>(Template)? 0 :
+ isa<FunctionTemplateDecl>(Template)? 1 :
+ isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
+ << Template << Range;
+
+ Invalid = true;
+ }
+
+ // C++ [temp.arg]p1:
+ // [...] The type and form of each template-argument specified in
+ // a template-id shall match the type and form specified for the
+ // corresponding parameter declared by the template in its
+ // template-parameter-list.
+ unsigned ArgIdx = 0;
+ for (TemplateParameterList::iterator Param = Params->begin(),
+ ParamEnd = Params->end();
+ Param != ParamEnd; ++Param, ++ArgIdx) {
+ // Decode the template argument
+ QualType ArgType;
+ Expr *ArgExpr = 0;
+ SourceLocation ArgLoc;
+ if (ArgIdx >= NumArgs) {
+ // FIXME: Get the default argument here, which might
+ // (eventually) require instantiation.
+ break;
+ } else
+ ArgLoc = TemplateArgLocs[ArgIdx];
+
+ if (Args.getArgIsType()[ArgIdx])
+ ArgType = QualType::getFromOpaquePtr(Args.getArgs()[ArgIdx]);
+ else
+ ArgExpr = reinterpret_cast<Expr *>(Args.getArgs()[ArgIdx]);
+
+ if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) {
+ // Check template type parameters.
+ if (!ArgType.isNull()) {
+ if (!CheckTemplateArgument(TTP, ArgType, ArgLoc))
+ Invalid = true;
+ continue;
+ }
+
+ // C++ [temp.arg.type]p1:
+ // A template-argument for a template-parameter which is a
+ // type shall be a type-id.
+
+ // We have a template type parameter but the template argument
+ // is an expression.
+ Diag(ArgExpr->getSourceRange().getBegin(),
+ diag::err_template_arg_must_be_type);
+ Diag((*Param)->getLocation(), diag::note_template_parameter_here);
+ Invalid = true;
+ } else if (NonTypeTemplateParmDecl *NTTP
+ = dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
+ // Check non-type template parameters.
+ if (ArgExpr) {
+ if (!CheckTemplateArgument(NTTP, ArgExpr))
+ Invalid = true;
+ continue;
+ }
+
+ // We have a non-type template parameter but the template
+ // argument is a type.
+
+ // C++ [temp.arg]p2:
+ // In a template-argument, an ambiguity between a type-id and
+ // an expression is resolved to a type-id, regardless of the
+ // form of the corresponding template-parameter.
+ //
+ // We warn specifically about this case, since it can be rather
+ // confusing for users.
+ if (ArgType->isFunctionType())
+ Diag(ArgLoc, diag::err_template_arg_nontype_ambig)
+ << ArgType;
+ else
+ Diag(ArgLoc, diag::err_template_arg_must_be_expr);
+ Diag((*Param)->getLocation(), diag::note_template_parameter_here);
+ Invalid = true;
+ } else {
+ // Check template template parameters.
+ TemplateTemplateParmDecl *TempParm
+ = cast<TemplateTemplateParmDecl>(*Param);
+
+ if (ArgExpr && isa<DeclRefExpr>(ArgExpr) &&
+ isa<TemplateDecl>(cast<DeclRefExpr>(ArgExpr)->getDecl())) {
+ if (!CheckTemplateArgument(TempParm, cast<DeclRefExpr>(ArgExpr)))
+ Invalid = true;
+ continue;
+ }
+
+ // We have a template template parameter but the template
+ // argument does not refer to a template.
+ Diag(ArgLoc, diag::err_template_arg_must_be_template);
+ Invalid = true;
+ }
+ }
+
+ return Invalid;
+}
+
+/// \brief Check a template argument against its corresponding
+/// template type parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.type]. It
+/// returns true if an error occurred, and false otherwise.
+bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param,
+ QualType Arg, SourceLocation ArgLoc) {
+ // C++ [temp.arg.type]p2:
+ // A local type, a type with no linkage, an unnamed type or a type
+ // compounded from any of these types shall not be used as a
+ // template-argument for a template type-parameter.
+ //
+ // FIXME: Perform the recursive and no-linkage type checks.
+ const TagType *Tag = 0;
+ if (const EnumType *EnumT = Arg->getAsEnumType())
+ Tag = EnumT;
+ else if (const RecordType *RecordT = Arg->getAsRecordType())
+ Tag = RecordT;
+ if (Tag && Tag->getDecl()->getDeclContext()->isFunctionOrMethod())
+ return Diag(ArgLoc, diag::err_template_arg_local_type)
+ << QualType(Tag, 0);
+ else if (Tag && !Tag->getDecl()->getDeclName()) {
+ Diag(ArgLoc, diag::err_template_arg_unnamed_type);
+ Diag(Tag->getDecl()->getLocation(), diag::note_template_unnamed_type_here);
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Check a template argument against its corresponding
+/// non-type template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.nontype].
+/// It returns true if an error occurred, and false otherwise.
+bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
+ Expr *Arg) {
+ return false;
+}
+
+/// \brief Check a template argument against its corresponding
+/// template template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.template].
+/// It returns true if an error occurred, and false otherwise.
+bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param,
+ DeclRefExpr *Arg) {
+ return false;
+}
+
/// \brief Determine whether the given template parameter lists are
/// equivalent.
///
diff --git a/test/SemaTemplate/temp_arg.cpp b/test/SemaTemplate/temp_arg.cpp
new file mode 100644
index 0000000000..d62f681f39
--- /dev/null
+++ b/test/SemaTemplate/temp_arg.cpp
@@ -0,0 +1,13 @@
+// RUN: clang -fsyntax-only -verify %s
+template<typename T,
+ int I,
+ template<typename> class TT>
+ class A;
+
+template<typename> class X;
+
+A<int, 0, X> * a1;
+
+A<float, 1, X, double> *a2; // expected-error{{too many template arguments for class template 'A'}}
+
+A<float, 1> *a3; // expected-error{{too few template arguments for class template 'A'}}
diff --git a/test/SemaTemplate/temp_arg_type.cpp b/test/SemaTemplate/temp_arg_type.cpp
new file mode 100644
index 0000000000..08b1032309
--- /dev/null
+++ b/test/SemaTemplate/temp_arg_type.cpp
@@ -0,0 +1,27 @@
+// RUN: clang -fsyntax-only -verify %s
+template<typename T> class A; // expected-error 2 {{template parameter is declared here}}
+
+// [temp.arg.type]p1
+A<0> *a1; // expected-error{{template argument for template type parameter must be a type}}
+
+A<A> *a2; // expected-error{{template argument for template type parameter must be a type}}
+
+A<int> *a3;
+// FIXME: The two below are well-formed, but we're not parsing them as type-ids.
+// A<int()> *a4;
+// A<int(float)> *a5;
+A<A<int> > *a6;
+
+// [temp.arg.type]p2
+void f() {
+ class X { };
+ A<X> * a = 0; // expected-error{{template argument uses local type 'class X'}}\
+ // FIXME: expected-error{{expected expression}}
+}
+
+struct { int x; } Unnamed; // expected-note{{unnamed type used in template argument was declared here}}
+A<__typeof__(Unnamed)> *a7; // expected-error{{template argument uses unnamed type}} \
+ // FIXME: expected-error{{expected unqualified-id}}
+
+// FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
+// belongs somewhere in the template instantiation section).