From f80a9d5c2ccc465211178223799217f7a42774ae Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Sat, 14 Mar 2009 00:20:21 +0000 Subject: [PATCH] Check for overflow and signedness problems with template arguments. Eliminates a FIXME. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66993 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.def | 6 +++ lib/Sema/SemaTemplate.cpp | 41 ++++++++++++++++----- test/SemaTemplate/temp_arg_nontype.cpp | 10 +++++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 8938a7ae63..be17a9746d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -604,6 +604,12 @@ 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_template_arg_negative, ERROR, + "non-type template argument provides negative value '%0' for unsigned " + "template parameter of type %1") +DIAG(err_template_arg_too_large, ERROR, + "non-type template argument value '%0' is too large for template " + "parameter of type %1") 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, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 142cc453f3..fd1eaf0ace 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1266,16 +1266,42 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return true; } - // FIXME: Check overflow of template arguments? + QualType IntegerType = Context.getCanonicalType(ParamType); + if (const EnumType *Enum = IntegerType->getAsEnumType()) + IntegerType = Enum->getDecl()->getIntegerType(); + + if (!Arg->isValueDependent()) { + // Check that an unsigned parameter does not receive a negative + // value. + if (IntegerType->isUnsignedIntegerType() + && (Value.isSigned() && Value.isNegative())) { + Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_negative) + << Value.toString(10) << Param->getType() + << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + + // Check that we don't overflow the template parameter type. + unsigned AllowedBits = Context.getTypeSize(IntegerType); + if (Value.getActiveBits() > AllowedBits) { + Diag(Arg->getSourceRange().getBegin(), + diag::err_template_arg_too_large) + << Value.toString(10) << Param->getType() + << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + + if (Value.getBitWidth() != AllowedBits) + Value.extOrTrunc(AllowedBits); + Value.setIsSigned(IntegerType->isSignedIntegerType()); + } if (Converted) { // Add the value of this argument to the list of converted // arguments. We use the bitwidth and signedness of the template // parameter. - QualType IntegerType = Context.getCanonicalType(ParamType); - if (const EnumType *Enum = IntegerType->getAsEnumType()) - IntegerType = Enum->getDecl()->getIntegerType(); - if (Arg->isValueDependent()) { // The argument is value-dependent. Create a new // TemplateArgument with the converted expression. @@ -1283,11 +1309,6 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return false; } - unsigned ExpectedBits = Context.getTypeSize(IntegerType); - if (Value.getBitWidth() != ExpectedBits) - Value.extOrTrunc(ExpectedBits); - Value.setIsSigned(IntegerType->isSignedIntegerType()); - Converted->push_back(TemplateArgument(StartLoc, Value, Context.getCanonicalType(IntegerType))); } diff --git a/test/SemaTemplate/temp_arg_nontype.cpp b/test/SemaTemplate/temp_arg_nontype.cpp index 55815fce1f..381947269e 100644 --- a/test/SemaTemplate/temp_arg_nontype.cpp +++ b/test/SemaTemplate/temp_arg_nontype.cpp @@ -102,3 +102,13 @@ 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::*'}} A7c<(&Z::int_member)> *a18_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}} + +template struct Overflow; // expected-note{{template parameter is declared here}} + +Overflow<5> *overflow1; // okay +Overflow<256> *overflow2; // expected-error{{non-type template argument value '256' is too large for template parameter of type 'unsigned char'}} + + +template struct Signedness; // expected-note{{template parameter is declared here}} +Signedness<10> *signedness1; // okay +Signedness<-10> *signedness2; // expected-error{{non-type template argument provides negative value '-10' for unsigned template parameter of type 'unsigned int'}} -- 2.40.0