From c15cb38a4ff717097b32532fbf761c71b1376a02 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 9 Feb 2009 23:23:08 +0000 Subject: [PATCH] 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 --- include/clang/Basic/DiagnosticSemaKinds.def | 22 ++- include/clang/Parse/Action.h | 2 + include/clang/Parse/Ownership.h | 2 +- include/clang/Parse/Parser.h | 4 +- lib/AST/Type.cpp | 2 +- lib/Parse/ParseTemplate.cpp | 23 ++- lib/Sema/Sema.h | 16 +- lib/Sema/SemaExpr.cpp | 4 +- lib/Sema/SemaTemplate.cpp | 182 ++++++++++++++++++++ test/SemaTemplate/temp_arg.cpp | 13 ++ test/SemaTemplate/temp_arg_type.cpp | 27 +++ 11 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 test/SemaTemplate/temp_arg.cpp create mode 100644 test/SemaTemplate/temp_arg_type.cpp 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 TemplateArgList; typedef llvm::SmallVector TemplateArgIsTypeList; + typedef llvm::SmallVector 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(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(D)) return Owned(BuildDeclRefExpr(Ovl, Context.OverloadTy, Loc, false, false, SS)); - + else if (TemplateDecl *Template = dyn_cast(D)) + return Owned(BuildDeclRefExpr(Template, Context.OverloadTy, Loc, + false, false, SS)); ValueDecl *VD = cast(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(static_cast(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(Template)? 0 : + isa(Template)? 1 : + isa(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(Args.getArgs()[ArgIdx]); + + if (TemplateTypeParmDecl *TTP = dyn_cast(*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(*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(*Param); + + if (ArgExpr && isa(ArgExpr) && + isa(cast(ArgExpr)->getDecl())) { + if (!CheckTemplateArgument(TempParm, cast(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 class TT> + class A; + +template class X; + +A * a1; + +A *a2; // expected-error{{too many template arguments for class template 'A'}} + +A *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 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 *a2; // expected-error{{template argument for template type parameter must be a type}} + +A *a3; +// FIXME: The two below are well-formed, but we're not parsing them as type-ids. +// A *a4; +// A *a5; +A > *a6; + +// [temp.arg.type]p2 +void f() { + class X { }; + A * 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). -- 2.40.0