Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6883,9 +6883,18 @@ "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< + "invalid literal operator parameter type %0">; +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_defined_type : Error< + "cannot define a literal operator on user-defined type %0">; +def err_literal_operator_template : Error< + "invalid template parameters for literal operator">; def err_literal_operator_extern_c : Error< "literal operator must have C++ linkage">; def ext_string_literal_operator_template : ExtWarn< Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -11780,10 +11780,9 @@ 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(); @@ -11793,8 +11792,9 @@ // 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(); + + // Must have one or two template parameters if (Params->size() == 1) { NonTypeTemplateParmDecl *PmDecl = dyn_cast(Params->getParam(0)); @@ -11802,7 +11802,8 @@ // The template parameter must be a char parameter pack. if (PmDecl && PmDecl->isTemplateParameterPack() && Context.hasSameType(PmDecl->getType(), Context.CharTy)) - Valid = true; + goto FinishedParams; + } else if (Params->size() == 2) { TemplateTypeParmDecl *PmType = dyn_cast(Params->getParam(0)); @@ -11811,82 +11812,135 @@ // The second template parameter must be a parameter pack with the // first template parameter as its type. - if (PmType && PmArgs && - !PmType->isTemplateParameterPack() && - PmArgs->isTemplateParameterPack()) { + if (PmType && PmArgs && !PmType->isTemplateParameterPack() && + PmArgs->isTemplateParameterPack()) { const TemplateTypeParmType *TArgs = PmArgs->getType()->getAs(); if (TArgs && TArgs->getDepth() == PmType->getDepth() && TArgs->getIndex() == PmType->getIndex()) { - Valid = true; if (ActiveTemplateInstantiations.empty()) Diag(FnDecl->getLocation(), diag::ext_string_literal_operator_template); + goto FinishedParams; } } + } else { + Diag(TpDecl->getLocation(), + diag::err_literal_operator_template_with_params); + return true; } } - } else if (FnDecl->param_size()) { - // Check the first parameter - FunctionDecl::param_iterator Param = FnDecl->param_begin(); - QualType T = (*Param)->getType().getUnqualifiedType(); - - // 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; + Diag(TpDecl->getTemplateParameters()->getSourceRange().getBegin(), + diag::err_literal_operator_template) + << TpDecl->getTemplateParameters()->getSourceRange(); + 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, and any character type are + // allowed as the only parameters. + if (Context.hasSameType(ParamType, Context.UnsignedLongLongTy) || + Context.hasSameType(ParamType, Context.LongDoubleTy) || + Context.hasSameType(ParamType, Context.CharTy) || + Context.hasSameType(ParamType, Context.WideCharTy) || + Context.hasSameType(ParamType, Context.Char16Ty) || + Context.hasSameType(ParamType, Context.Char32Ty)) { goto FinishedParams; + } else if (const PointerType *Ptr = ParamType->getAs()) { + QualType InnerType = Ptr->getPointeeType(); + + if (Context.hasSameType(InnerType.getUnqualifiedType(), Context.CharTy) && + InnerType.isConstQualified() && !InnerType.isVolatileQualified()) + goto FinishedParams; + + Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param) + << ParamType << "'const char *'" << Param->getSourceRange(); + + } else if (ParamType->isRealFloatingType()) { + Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param) + << ParamType << Context.LongDoubleTy << Param->getSourceRange(); + + } else if (ParamType->isIntegerType()) { + Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param) + << ParamType << Context.UnsignedLongLongTy << Param->getSourceRange(); + + } else if (!ParamType->isBuiltinType()) { + Diag(Param->getSourceRange().getBegin(), + diag::err_literal_operator_defined_type) + << ParamType << Param->getSourceRange(); + + } else { + Diag(Param->getSourceRange().getBegin(), + diag::err_literal_operator_invalid_param) + << ParamType << Param->getSourceRange(); } - // Otherwise it must be a pointer to const; let's strip those qualifiers. - const PointerType *PT = T->getAs(); - if (!PT) - goto FinishedParams; - T = PT->getPointeeType(); - if (!T.isConstQualified() || T.isVolatileQualified()) - goto FinishedParams; - T = T.getUnqualifiedType(); + return true; - // Move on to the second parameter; - ++Param; + } else if (FnDecl->param_size() == 2) { + FunctionDecl::param_iterator Param = FnDecl->param_begin(); - // 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; + // First, verify that the first parameter is correct. + + 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(); + + if (!PT) { + Diag((*Param)->getSourceRange().getBegin(), + diag::err_literal_operator_param) + << FirstParamType << "'const char *'" << (*Param)->getSourceRange(); + return true; + } + + 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; + 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; + } - // 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; - } + // Move on to the second and final parameter. + ++Param; - // FIXME: This diagnostic is absolutely terrible. -FinishedParams: - if (!Valid) { - Diag(FnDecl->getLocation(), diag::err_literal_operator_params) - << FnDecl->getDeclName(); + // The second parameter must be a std::size_t. + QualType SecondParamType = (*Param)->getType().getUnqualifiedType(); + if (Context.hasSameType(SecondParamType, Context.getSizeType())) + goto FinishedParams; + else { + 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; } +FinishedParams: // A parameter-declaration-clause containing a default argument is not // equivalent to any of the permitted forms. for (auto Param : FnDecl->params()) { Index: test/CXX/over/over.oper/over.literal/p5.cpp =================================================================== --- test/CXX/over/over.oper/over.literal/p5.cpp +++ test/CXX/over/over.oper/over.literal/p5.cpp @@ -12,11 +12,11 @@ friend U operator "" _a(const T *, size_t); // expected-error {{parameter}} }; template struct V { - friend void operator "" _b(); // expected-error {{parameter}} + friend void operator "" _b(); // expected-error {{template}} }; -template void operator "" _b(); // expected-error {{parameter}} -template void operator "" _b(int N = 0); // expected-error {{parameter}} -template void operator "" _b(); // expected-error {{parameter}} -template T operator "" _b(const char *); // expected-error {{parameter}} -template int operator "" _b(const T *, size_t); // expected-error {{parameter}} +template void operator "" _b(); // expected-error {{template}} +template void operator "" _b(int N = 0); // expected-error {{template}} +template void operator "" _b(); // expected-error {{template}} +template T operator "" _b(const char *); // expected-error {{template}} +template int operator "" _b(const T *, size_t); // expected-error {{template}} Index: test/CXX/over/over.oper/over.literal/p8.cpp =================================================================== --- test/CXX/over/over.oper/over.literal/p8.cpp +++ test/CXX/over/over.oper/over.literal/p8.cpp @@ -12,6 +12,6 @@ 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 int operator "" j(const char*); // expected-error {{parameter}} +template int operator "" j(const char*); // expected-error {{template}} float operator ""_E(const char *); Index: test/SemaCXX/literal-operators.cpp =================================================================== --- test/SemaCXX/literal-operators.cpp +++ test/SemaCXX/literal-operators.cpp @@ -35,13 +35,14 @@ 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 void operator "" _good (); -// FIXME: Test some invalid decls that might crop up. -template void operator "" _invalid(); // expected-error {{parameter declaration for literal operator 'operator""_invalid' is not valid}} +template void operator "" _invalid(); // expected-error {{invalid template parameters for literal operator}} +template void operator "" _invalid(); // expected-error {{invalid template parameters for literal operator}} +template void operator "" _invalid(); // expected-error {{invalid template parameters for literal operator}} _Complex float operator""if(long double); // expected-warning {{reserved}} _Complex float test_if_1() { return 2.0f + 1.5if; };