forked from OSchip/llvm-project
Improve diagnostics for ill-formed literal operator declarations.
Patch by Erik Pilkington! llvm-svn: 261034
This commit is contained in:
parent
b68464eb78
commit
c28aee6a51
clang
include/clang/Basic
lib/Sema
test
|
@ -6886,9 +6886,16 @@ def err_literal_operator_id_outside_namespace : Error<
|
|||
"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<
|
||||
|
|
|
@ -11765,6 +11765,49 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
|
|||
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::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) {
|
|||
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::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) {
|
|||
// 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;
|
||||
}
|
||||
} else if (FnDecl->param_size()) {
|
||||
// Check the first parameter
|
||||
|
||||
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() == 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;
|
||||
QualType FirstParamType = (*Param)->getType().getUnqualifiedType();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
// Move on to the second parameter;
|
||||
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(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;
|
||||
}
|
||||
|
||||
// Move on to the second and final parameter.
|
||||
++Param;
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// FIXME: This diagnostic is absolutely terrible.
|
||||
FinishedParams:
|
||||
if (!Valid) {
|
||||
Diag(FnDecl->getLocation(), diag::err_literal_operator_params)
|
||||
<< FnDecl->getDeclName();
|
||||
} 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()) {
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -12,6 +12,6 @@ float operator ""E(const char *); // expected-error {{invalid suffix on literal}
|
|||
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 *);
|
||||
|
|
|
@ -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; };
|
||||
|
|
Loading…
Reference in New Issue