r261034 - Improve diagnostics for ill-formed literal operator declarations.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 16 16:04:04 PST 2016
Author: rsmith
Date: Tue Feb 16 18:04:04 2016
New Revision: 261034
URL: http://llvm.org/viewvc/llvm-project?rev=261034&view=rev
Log:
Improve diagnostics for ill-formed literal operator declarations.
Patch by Erik Pilkington!
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/test/CXX/over/over.oper/over.literal/p5.cpp
cfe/trunk/test/CXX/over/over.oper/over.literal/p8.cpp
cfe/trunk/test/SemaCXX/literal-operators.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=261034&r1=261033&r2=261034&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Feb 16 18:04:04 2016
@@ -6886,9 +6886,16 @@ def err_literal_operator_id_outside_name
"non-namespace scope '%0' cannot have a literal operator member">;
def err_literal_operator_default_argument : Error<
"literal operator cannot have a default argument">;
-// FIXME: This diagnostic sucks
-def err_literal_operator_params : Error<
- "parameter declaration for literal operator %0 is not valid">;
+def err_literal_operator_bad_param_count : Error<
+ "non-template literal operator must have one or two parameters">;
+def err_literal_operator_invalid_param : Error<
+ "parameter of literal operator must have type 'unsigned long long', 'long double', 'char', 'wchar_t', 'char16_t', 'char32_t', or 'const char *'">;
+def err_literal_operator_param : Error<
+ "invalid literal operator parameter type %0, did you mean %1?">;
+def err_literal_operator_template_with_params : Error<
+ "literal operator template cannot have any parameters">;
+def err_literal_operator_template : Error<
+ "template parameter list for literal operator must be either 'char...' or 'typename T, T...'">;
def err_literal_operator_extern_c : Error<
"literal operator must have C++ linkage">;
def ext_string_literal_operator_template : ExtWarn<
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=261034&r1=261033&r2=261034&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Tue Feb 16 18:04:04 2016
@@ -11765,6 +11765,49 @@ bool Sema::CheckOverloadedOperatorDeclar
return false;
}
+static bool
+checkLiteralOperatorTemplateParameterList(Sema &SemaRef,
+ FunctionTemplateDecl *TpDecl) {
+ TemplateParameterList *TemplateParams = TpDecl->getTemplateParameters();
+
+ // Must have one or two template parameters.
+ if (TemplateParams->size() == 1) {
+ NonTypeTemplateParmDecl *PmDecl =
+ dyn_cast<NonTypeTemplateParmDecl>(TemplateParams->getParam(0));
+
+ // The template parameter must be a char parameter pack.
+ if (PmDecl && PmDecl->isTemplateParameterPack() &&
+ SemaRef.Context.hasSameType(PmDecl->getType(), SemaRef.Context.CharTy))
+ return false;
+
+ } else if (TemplateParams->size() == 2) {
+ TemplateTypeParmDecl *PmType =
+ dyn_cast<TemplateTypeParmDecl>(TemplateParams->getParam(0));
+ NonTypeTemplateParmDecl *PmArgs =
+ dyn_cast<NonTypeTemplateParmDecl>(TemplateParams->getParam(1));
+
+ // The second template parameter must be a parameter pack with the
+ // first template parameter as its type.
+ if (PmType && PmArgs && !PmType->isTemplateParameterPack() &&
+ PmArgs->isTemplateParameterPack()) {
+ const TemplateTypeParmType *TArgs =
+ PmArgs->getType()->getAs<TemplateTypeParmType>();
+ if (TArgs && TArgs->getDepth() == PmType->getDepth() &&
+ TArgs->getIndex() == PmType->getIndex()) {
+ if (SemaRef.ActiveTemplateInstantiations.empty())
+ SemaRef.Diag(TpDecl->getLocation(),
+ diag::ext_string_literal_operator_template);
+ return false;
+ }
+ }
+ }
+
+ SemaRef.Diag(TpDecl->getTemplateParameters()->getSourceRange().getBegin(),
+ diag::err_literal_operator_template)
+ << TpDecl->getTemplateParameters()->getSourceRange();
+ return true;
+}
+
/// CheckLiteralOperatorDeclaration - Check whether the declaration
/// of this literal operator function is well-formed. If so, returns
/// false; otherwise, emits appropriate diagnostics and returns true.
@@ -11780,10 +11823,9 @@ bool Sema::CheckLiteralOperatorDeclarati
return true;
}
- bool Valid = false;
-
// This might be the definition of a literal operator template.
FunctionTemplateDecl *TpDecl = FnDecl->getDescribedFunctionTemplate();
+
// This might be a specialization of a literal operator template.
if (!TpDecl)
TpDecl = FnDecl->getPrimaryTemplate();
@@ -11792,101 +11834,117 @@ bool Sema::CheckLiteralOperatorDeclarati
// template <class T, T...> type operator "" name() are the only valid
// template signatures, and the only valid signatures with no parameters.
if (TpDecl) {
- if (FnDecl->param_size() == 0) {
- // Must have one or two template parameters
- TemplateParameterList *Params = TpDecl->getTemplateParameters();
- if (Params->size() == 1) {
- NonTypeTemplateParmDecl *PmDecl =
- dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(0));
-
- // The template parameter must be a char parameter pack.
- if (PmDecl && PmDecl->isTemplateParameterPack() &&
- Context.hasSameType(PmDecl->getType(), Context.CharTy))
- Valid = true;
- } else if (Params->size() == 2) {
- TemplateTypeParmDecl *PmType =
- dyn_cast<TemplateTypeParmDecl>(Params->getParam(0));
- NonTypeTemplateParmDecl *PmArgs =
- dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(1));
-
- // The second template parameter must be a parameter pack with the
- // first template parameter as its type.
- if (PmType && PmArgs &&
- !PmType->isTemplateParameterPack() &&
- PmArgs->isTemplateParameterPack()) {
- const TemplateTypeParmType *TArgs =
- PmArgs->getType()->getAs<TemplateTypeParmType>();
- if (TArgs && TArgs->getDepth() == PmType->getDepth() &&
- TArgs->getIndex() == PmType->getIndex()) {
- Valid = true;
- if (ActiveTemplateInstantiations.empty())
- Diag(FnDecl->getLocation(),
- diag::ext_string_literal_operator_template);
- }
- }
+ if (FnDecl->param_size() != 0) {
+ Diag(FnDecl->getLocation(),
+ diag::err_literal_operator_template_with_params);
+ return true;
+ }
+
+ if (checkLiteralOperatorTemplateParameterList(*this, TpDecl))
+ return true;
+
+ } else if (FnDecl->param_size() == 1) {
+ const ParmVarDecl *Param = FnDecl->getParamDecl(0);
+
+ QualType ParamType = Param->getType().getUnqualifiedType();
+
+ // Only unsigned long long int, long double, any character type, and const
+ // char * are allowed as the only parameters.
+ if (ParamType->isSpecificBuiltinType(BuiltinType::ULongLong) ||
+ ParamType->isSpecificBuiltinType(BuiltinType::LongDouble) ||
+ Context.hasSameType(ParamType, Context.CharTy) ||
+ Context.hasSameType(ParamType, Context.WideCharTy) ||
+ Context.hasSameType(ParamType, Context.Char16Ty) ||
+ Context.hasSameType(ParamType, Context.Char32Ty)) {
+ } else if (const PointerType *Ptr = ParamType->getAs<PointerType>()) {
+ QualType InnerType = Ptr->getPointeeType();
+
+ // Pointer parameter must be a const char *.
+ if (!(Context.hasSameType(InnerType.getUnqualifiedType(),
+ Context.CharTy) &&
+ InnerType.isConstQualified() && !InnerType.isVolatileQualified())) {
+ Diag(Param->getSourceRange().getBegin(),
+ diag::err_literal_operator_param)
+ << ParamType << "'const char *'" << Param->getSourceRange();
+ return true;
}
+
+ } else if (ParamType->isRealFloatingType()) {
+ Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param)
+ << ParamType << Context.LongDoubleTy << Param->getSourceRange();
+ return true;
+
+ } else if (ParamType->isIntegerType()) {
+ Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param)
+ << ParamType << Context.UnsignedLongLongTy << Param->getSourceRange();
+ return true;
+
+ } else {
+ Diag(Param->getSourceRange().getBegin(),
+ diag::err_literal_operator_invalid_param)
+ << ParamType << Param->getSourceRange();
+ return true;
}
- } else if (FnDecl->param_size()) {
- // Check the first parameter
+
+ } else if (FnDecl->param_size() == 2) {
FunctionDecl::param_iterator Param = FnDecl->param_begin();
- QualType T = (*Param)->getType().getUnqualifiedType();
+ // First, verify that the first parameter is correct.
- // unsigned long long int, long double, and any character type are allowed
- // as the only parameters.
- if (Context.hasSameType(T, Context.UnsignedLongLongTy) ||
- Context.hasSameType(T, Context.LongDoubleTy) ||
- Context.hasSameType(T, Context.CharTy) ||
- Context.hasSameType(T, Context.WideCharTy) ||
- Context.hasSameType(T, Context.Char16Ty) ||
- Context.hasSameType(T, Context.Char32Ty)) {
- if (++Param == FnDecl->param_end())
- Valid = true;
- goto FinishedParams;
- }
-
- // Otherwise it must be a pointer to const; let's strip those qualifiers.
- const PointerType *PT = T->getAs<PointerType>();
- if (!PT)
- goto FinishedParams;
- T = PT->getPointeeType();
- if (!T.isConstQualified() || T.isVolatileQualified())
- goto FinishedParams;
- T = T.getUnqualifiedType();
+ QualType FirstParamType = (*Param)->getType().getUnqualifiedType();
- // Move on to the second parameter;
- ++Param;
+ // Two parameter function must have a pointer to const as a
+ // first parameter; let's strip those qualifiers.
+ const PointerType *PT = FirstParamType->getAs<PointerType>();
+
+ if (!PT) {
+ Diag((*Param)->getSourceRange().getBegin(),
+ diag::err_literal_operator_param)
+ << FirstParamType << "'const char *'" << (*Param)->getSourceRange();
+ return true;
+ }
- // If there is no second parameter, the first must be a const char *
- if (Param == FnDecl->param_end()) {
- if (Context.hasSameType(T, Context.CharTy))
- Valid = true;
- goto FinishedParams;
+ QualType PointeeType = PT->getPointeeType();
+ // First parameter must be const
+ if (!PointeeType.isConstQualified() || PointeeType.isVolatileQualified()) {
+ Diag((*Param)->getSourceRange().getBegin(),
+ diag::err_literal_operator_param)
+ << FirstParamType << "'const char *'" << (*Param)->getSourceRange();
+ return true;
}
- // const char *, const wchar_t*, const char16_t*, and const char32_t*
+ QualType InnerType = PointeeType.getUnqualifiedType();
+ // Only const char *, const wchar_t*, const char16_t*, and const char32_t*
// are allowed as the first parameter to a two-parameter function
- if (!(Context.hasSameType(T, Context.CharTy) ||
- Context.hasSameType(T, Context.WideCharTy) ||
- Context.hasSameType(T, Context.Char16Ty) ||
- Context.hasSameType(T, Context.Char32Ty)))
- goto FinishedParams;
-
- // The second and final parameter must be an std::size_t
- T = (*Param)->getType().getUnqualifiedType();
- if (Context.hasSameType(T, Context.getSizeType()) &&
- ++Param == FnDecl->param_end())
- Valid = true;
- }
+ if (!(Context.hasSameType(InnerType, Context.CharTy) ||
+ Context.hasSameType(InnerType, Context.WideCharTy) ||
+ Context.hasSameType(InnerType, Context.Char16Ty) ||
+ Context.hasSameType(InnerType, Context.Char32Ty))) {
+ Diag((*Param)->getSourceRange().getBegin(),
+ diag::err_literal_operator_param)
+ << FirstParamType << "'const char *'" << (*Param)->getSourceRange();
+ return true;
+ }
- // FIXME: This diagnostic is absolutely terrible.
-FinishedParams:
- if (!Valid) {
- Diag(FnDecl->getLocation(), diag::err_literal_operator_params)
- << FnDecl->getDeclName();
+ // Move on to the second and final parameter.
+ ++Param;
+
+ // The second parameter must be a std::size_t.
+ QualType SecondParamType = (*Param)->getType().getUnqualifiedType();
+ if (!Context.hasSameType(SecondParamType, Context.getSizeType())) {
+ Diag((*Param)->getSourceRange().getBegin(),
+ diag::err_literal_operator_param)
+ << SecondParamType << Context.getSizeType()
+ << (*Param)->getSourceRange();
+ return true;
+ }
+ } else {
+ Diag(FnDecl->getLocation(), diag::err_literal_operator_bad_param_count);
return true;
}
+ // Parameters are good.
+
// A parameter-declaration-clause containing a default argument is not
// equivalent to any of the permitted forms.
for (auto Param : FnDecl->params()) {
Modified: cfe/trunk/test/CXX/over/over.oper/over.literal/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.oper/over.literal/p5.cpp?rev=261034&r1=261033&r2=261034&view=diff
==============================================================================
--- cfe/trunk/test/CXX/over/over.oper/over.literal/p5.cpp (original)
+++ cfe/trunk/test/CXX/over/over.oper/over.literal/p5.cpp Tue Feb 16 18:04:04 2016
@@ -12,11 +12,11 @@ template<typename T> struct U {
friend U operator "" _a(const T *, size_t); // expected-error {{parameter}}
};
template<char...> struct V {
- friend void operator "" _b(); // expected-error {{parameter}}
+ friend void operator "" _b(); // expected-error {{parameters}}
};
-template<char... C, int N = 0> void operator "" _b(); // expected-error {{parameter}}
-template<char... C> void operator "" _b(int N = 0); // expected-error {{parameter}}
-template<char, char...> void operator "" _b(); // expected-error {{parameter}}
-template<typename T> T operator "" _b(const char *); // expected-error {{parameter}}
-template<typename T> int operator "" _b(const T *, size_t); // expected-error {{parameter}}
+template<char... C, int N = 0> void operator "" _b(); // expected-error {{template}}
+template<char... C> void operator "" _b(int N = 0); // expected-error {{template}}
+template<char, char...> void operator "" _b(); // expected-error {{template}}
+template<typename T> T operator "" _b(const char *); // expected-error {{template}}
+template<typename T> int operator "" _b(const T *, size_t); // expected-error {{template}}
Modified: cfe/trunk/test/CXX/over/over.oper/over.literal/p8.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.oper/over.literal/p8.cpp?rev=261034&r1=261033&r2=261034&view=diff
==============================================================================
--- cfe/trunk/test/CXX/over/over.oper/over.literal/p8.cpp (original)
+++ cfe/trunk/test/CXX/over/over.oper/over.literal/p8.cpp Tue Feb 16 18:04:04 2016
@@ -12,6 +12,6 @@ float operator ""E(const char *); // exp
float operator " " B(const char *); // expected-error {{must be '""'}} expected-warning {{reserved}}
string operator "" 5X(const char *, std::size_t); // expected-error {{expected identifier}}
double operator "" _miles(double); // expected-error {{parameter}}
-template<char...> int operator "" j(const char*); // expected-error {{parameter}}
+template<char...> int operator "" j(const char*); // expected-error {{template}}
float operator ""_E(const char *);
Modified: cfe/trunk/test/SemaCXX/literal-operators.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/literal-operators.cpp?rev=261034&r1=261033&r2=261034&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/literal-operators.cpp (original)
+++ cfe/trunk/test/SemaCXX/literal-operators.cpp Tue Feb 16 18:04:04 2016
@@ -35,13 +35,14 @@ typedef const char c;
void operator "" _good (c*);
// Check extra cv-qualifiers
-void operator "" _cv_good (volatile const char *, const size_t); // expected-error {{parameter declaration for literal operator 'operator""_cv_good' is not valid}}
+void operator "" _cv_good (volatile const char *, const size_t); // expected-error {{invalid literal operator parameter type 'const volatile char *', did you mean 'const char *'?}}
// Template declaration
template <char...> void operator "" _good ();
-// FIXME: Test some invalid decls that might crop up.
-template <typename...> void operator "" _invalid(); // expected-error {{parameter declaration for literal operator 'operator""_invalid' is not valid}}
+template <typename...> void operator "" _invalid(); // expected-error {{template parameter list for literal operator must be either 'char...' or 'typename T, T...'}}
+template <wchar_t...> void operator "" _invalid(); // expected-error {{template parameter list for literal operator must be either 'char...' or 'typename T, T...'}}
+template <unsigned long long...> void operator "" _invalid(); // expected-error {{template parameter list for literal operator must be either 'char...' or 'typename T, T...'}}
_Complex float operator""if(long double); // expected-warning {{reserved}}
_Complex float test_if_1() { return 2.0f + 1.5if; };
More information about the cfe-commits
mailing list