Rudimentary checking of template arguments against their corresponding

template parameters when performing semantic analysis of a template-id
naming a class template specialization.

llvm-svn: 64185
This commit is contained in:
Douglas Gregor 2009-02-09 23:23:08 +00:00
parent 3af42a8a14
commit d32e028f79
11 changed files with 285 additions and 12 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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
}

View File

@ -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);
//===--------------------------------------------------------------------===//

View File

@ -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;
}
//===----------------------------------------------------------------------===//

View File

@ -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;

View File

@ -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,

View File

@ -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)).

View File

@ -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.
///

View File

@ -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'}}

View File

@ -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).