From: Douglas Gregor Date: Tue, 10 Feb 2009 23:36:10 +0000 (+0000) Subject: Add type-checking and implicit conversions for template parameters of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6ae5e6649f5d01a1b593f4db755bfcb42e095700;p=clang Add type-checking and implicit conversions for template parameters of integral or enumeration type. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64256 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 5dfe9d6f35..a4aa949822 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -525,6 +525,12 @@ DIAG(note_template_arg_refers_here, 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") +DIAG(err_template_arg_not_integral_or_enumeral, ERROR, + "non-type template argument of type %0 must have an integral or enumeration type") +DIAG(err_template_arg_not_ice, ERROR, + "non-type template argument of type %0 is not an integral constant expression") +DIAG(err_template_arg_not_convertible, ERROR, + "non-type template argument of type %0 cannot be converted to a value of type %1") DIAG(err_unexpected_typedef, ERROR, "unexpected type name %0: expected expression") diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 669fd92584..5ee14faaca 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -281,6 +281,9 @@ public: SourceRange Range2 = SourceRange(), QualType PrintType = QualType()); + bool hasSameType(QualType T1, QualType T2); + bool hasSameUnqualifiedType(QualType T1, QualType T2); + //===--------------------------------------------------------------------===// // Symbol table / Decl tracking callbacks: SemaDecl.cpp. // @@ -1547,7 +1550,7 @@ public: bool CheckTemplateArgument(TemplateTypeParmDecl *Param, QualType Arg, SourceLocation ArgLoc); - bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Expr *Arg); + bool CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Expr *&Arg); bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, DeclRefExpr *Arg); bool TemplateParameterListsAreEqual(TemplateParameterList *New, TemplateParameterList *Old, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 5eb2b2145c..7aab4a585e 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -827,7 +827,75 @@ bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param, /// 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) { + Expr *&Arg) { + // If either the parameter has a dependent type or the argument is + // type-dependent, there's nothing we can check now. + if (Param->getType()->isDependentType() || Arg->isTypeDependent()) + return false; + + // C++ [temp.arg.nontype]p5: + // The following conversions are performed on each expression used + // as a non-type template-argument. If a non-type + // template-argument cannot be converted to the type of the + // corresponding template-parameter then the program is + // ill-formed. + // + // -- for a non-type template-parameter of integral or + // enumeration type, integral promotions (4.5) and integral + // conversions (4.7) are applied. + QualType ParamType = Param->getType(); + if (ParamType->isIntegralType() || ParamType->isEnumeralType()) { + QualType ArgType = Arg->getType(); + + // C++ [temp.arg.nontype]p1: + // A template-argument for a non-type, non-template + // template-parameter shall be one of: + // + // -- an integral constant-expression of integral or enumeration + // type; or + // -- the name of a non-type template-parameter; or + SourceLocation NonConstantLoc; + if (!ArgType->isIntegralType() && !ArgType->isEnumeralType()) { + Diag(Arg->getSourceRange().getBegin(), + diag::err_template_arg_not_integral_or_enumeral) + << ArgType << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } else if (!Arg->isValueDependent() && + !Arg->isIntegerConstantExpr(Context, &NonConstantLoc)) { + Diag(NonConstantLoc, diag::err_template_arg_not_ice) + << ArgType << Arg->getSourceRange(); + return true; + } + + // FIXME: We need some way to more easily get the unqualified form + // of the types without going all the way to the + // canonical type. + if (Context.getCanonicalType(ParamType).getCVRQualifiers()) + ParamType = Context.getCanonicalType(ParamType).getUnqualifiedType(); + if (Context.getCanonicalType(ArgType).getCVRQualifiers()) + ArgType = Context.getCanonicalType(ArgType).getUnqualifiedType(); + + // Try to convert the argument to the parameter's type. + if (ParamType == ArgType) { + // Okay: no conversion necessary + } else if (IsIntegralPromotion(Arg, ArgType, ParamType) || + !ParamType->isEnumeralType()) { + // This is an integral promotion or conversion. + ImpCastExprToType(Arg, ParamType); + } else { + // We can't perform this conversion. + Diag(Arg->getSourceRange().getBegin(), + diag::err_template_arg_not_convertible) + << Arg->getType() << Param->getType() << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + + return false; + } + // FIXME: p5 has a lot more checks to perform! + return false; } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index e898782f31..7049a301f7 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -835,3 +835,17 @@ bool Sema::DiagnoseIncompleteType(SourceLocation Loc, QualType T, unsigned diag, return true; } + +/// \brief Determine whether the given types are equivalent. +bool Sema::hasSameType(QualType T1, QualType T2) { + return Context.getCanonicalType(T1) == Context.getCanonicalType(T2); +} + +/// \brief Determine whether the given types are equivalent after +/// cvr-qualifiers have been removed. +bool Sema::hasSameUnqualifiedType(QualType T1, QualType T2) { + T1 = Context.getCanonicalType(T1); + T2 = Context.getCanonicalType(T2); + return T1.getUnqualifiedType() == T2.getUnqualifiedType(); +} + diff --git a/test/SemaTemplate/temp_arg_nontype.cpp b/test/SemaTemplate/temp_arg_nontype.cpp index 8cbede4cd0..46d92c537b 100644 --- a/test/SemaTemplate/temp_arg_nontype.cpp +++ b/test/SemaTemplate/temp_arg_nontype.cpp @@ -1,6 +1,5 @@ // RUN: clang -fsyntax-only -std=c++98 -verify %s - -template struct A; // expected-note 2{{template parameter is declared here}} +template struct A; // expected-note 5{{template parameter is declared here}} A<0> *a0; @@ -10,5 +9,32 @@ A *a2; // expected-error{{template argument for non-type template parameter A<1 >> 2> *a3; -// FIXME: We haven't tried actually checking the expressions yet. -// A *a4; +// C++ [temp.arg.nontype]p5: +A *a4; // expected-error{{must have an integral or enumeration type}} \ + // FIXME: the error message above is a bit lame \ + // FIXME: expected-error{{expected unqualified-id}} + +enum E { Enumerator = 17 }; +A *a5; // expected-error{{template argument for non-type template parameter must be an expression}} + +template struct A1; // expected-note{{template parameter is declared here}} +A1 *a6; // okay +A1<17> *a7; // expected-error{{non-type template argument of type 'int' cannot be converted to a value of type 'enum E'}} \ + // FIXME: expected-error{{expected unqualified-id}} + +const long LongValue = 12345678; +A *a8; +const short ShortValue = 17; +A *a9; + +int f(int); +A *a10; // expected-error{{non-type template argument of type 'int' is not an integral constant expression}} \ + // FIXME: expected-error{{expected unqualified-id}} + +class X { +public: + X(int, int); + operator int() const; +}; +A *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}} \ + // FIXME:expected-error{{expected unqualified-id}}