diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 2c95b52a693f90fbb1ca26411b09234c412577a0..fb2092197d85125b53210ea6b25353b50cb4fa3d 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -59,6 +59,8 @@ def GNUConditionalOmittedOperand : DiagGroup<"gnu-conditional-omitted-operand">; def ConfigMacros : DiagGroup<"config-macros">; def : DiagGroup<"ctor-dtor-privacy">; def GNUDesignator : DiagGroup<"gnu-designator">; +def GNUStringLiteralOperatorTemplate : + DiagGroup<"gnu-string-literal-operator-template">; def DeleteNonVirtualDtor : DiagGroup<"delete-non-virtual-dtor">; def AbstractFinalClass : DiagGroup<"abstract-final-class">; @@ -561,6 +563,7 @@ def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct, GNUImaginaryConstant, GNULabelsAsValue, RedeclaredClassMember, GNURedeclaredEnum, GNUStatementExpression, GNUStaticFloatInit, + GNUStringLiteralOperatorTemplate, GNUUnionCast, GNUVariableSizedTypeNotAtEnd, ZeroLengthArray, GNUZeroLineDirective, GNUZeroVariadicMacroArguments]>; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index f9a5e1ab36193a5da3fbfbd7fc7f92a9663b59f4..4a77004c9e7333df66023588f3095eaab0603b8a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2811,7 +2811,8 @@ def err_addr_ovl_no_qualifier : Error< def err_ovl_no_viable_literal_operator : Error< "no matching literal operator for call to %0" "%select{| with argument of type %2| with arguments of types %2 and %3}1" - "%select{| or 'const char *', and no matching literal operator template}4">; + "%select{| or 'const char *'}4" + "%select{|, and no matching literal operator template}5">; // C++ Template Declarations def err_template_param_shadow : Error< @@ -5913,6 +5914,9 @@ def err_literal_operator_params : Error< "parameter declaration for literal operator %0 is not valid">; def err_literal_operator_extern_c : Error< "literal operator must have C++ linkage">; +def ext_string_literal_operator_template : ExtWarn< + "string literal operator templates are a GNU extension">, + InGroup<GNUStringLiteralOperatorTemplate>; def warn_user_literal_reserved : Warning< "user-defined literal suffixes not starting with '_' are reserved" "%select{; no literal will invoke this operator|}0">, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6ba53d547dda931a5ed57c780d1cd810894bacd5..82fa69cba080609d76d01d0df276522bb6fd9eb9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2400,7 +2400,11 @@ public: /// \brief The lookup found an overload set of literal operator templates, /// which expect the characters of the spelling of the literal token to be /// passed as a non-type template argument pack. - LOLR_Template + LOLR_Template, + /// \brief The lookup found an overload set of literal operator templates, + /// which expect the character type and characters of the spelling of the + /// string literal token to be passed as template arguments. + LOLR_StringTemplate }; SpecialMemberOverloadResult *LookupSpecialMember(CXXRecordDecl *D, @@ -2467,7 +2471,9 @@ public: LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef<QualType> ArgTys, - bool AllowRawAndTemplate); + bool AllowRaw, + bool AllowTemplate, + bool AllowStringTemplate); bool isKnownName(StringRef name); void ArgumentDependentLookup(DeclarationName Name, bool Operator, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 518f3156a9fb2e7c06235699fe2b2805d631f87a..6d357e7be05628742bd313c6ceafcebb462499e5 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -10917,11 +10917,12 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) { if (!TpDecl) TpDecl = FnDecl->getPrimaryTemplate(); - // template <char...> type operator "" name() is the only valid template - // signature, and the only valid signature with no parameters. + // template <char...> type operator "" name() and + // 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 only one template parameter + // Must have one or two template parameters TemplateParameterList *Params = TpDecl->getTemplateParameters(); if (Params->size() == 1) { NonTypeTemplateParmDecl *PmDecl = @@ -10931,6 +10932,27 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) { 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); + } + } } } } else if (FnDecl->param_size()) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 9188808bcef2782b6713ac886b41c6fd4fbaa5fa..12e6a260ac9726013506cee2e7b0c05109c707df 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1461,7 +1461,8 @@ static ExprResult BuildCookedLiteralOperatorCall(Sema &S, Scope *Scope, LookupResult R(S, OpName, UDSuffixLoc, Sema::LookupOrdinaryName); if (S.LookupLiteralOperator(Scope, R, llvm::makeArrayRef(ArgTy, Args.size()), - /*AllowRawAndTemplate*/false) == Sema::LOLR_Error) + /*AllowRaw*/false, /*AllowTemplate*/false, + /*AllowStringTemplate*/false) == Sema::LOLR_Error) return ExprError(); return S.BuildLiteralOperatorCall(R, OpNameInfo, Args, LitEndLoc); @@ -1486,36 +1487,34 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks, for (unsigned i = 0; i != NumStringToks; ++i) StringTokLocs.push_back(StringToks[i].getLocation()); - QualType StrTy = Context.CharTy; - if (Literal.isWide()) - StrTy = Context.getWideCharType(); - else if (Literal.isUTF16()) - StrTy = Context.Char16Ty; - else if (Literal.isUTF32()) - StrTy = Context.Char32Ty; - else if (Literal.isPascal()) - StrTy = Context.UnsignedCharTy; - + QualType CharTy = Context.CharTy; StringLiteral::StringKind Kind = StringLiteral::Ascii; - if (Literal.isWide()) + if (Literal.isWide()) { + CharTy = Context.getWideCharType(); Kind = StringLiteral::Wide; - else if (Literal.isUTF8()) + } else if (Literal.isUTF8()) { Kind = StringLiteral::UTF8; - else if (Literal.isUTF16()) + } else if (Literal.isUTF16()) { + CharTy = Context.Char16Ty; Kind = StringLiteral::UTF16; - else if (Literal.isUTF32()) + } else if (Literal.isUTF32()) { + CharTy = Context.Char32Ty; Kind = StringLiteral::UTF32; + } else if (Literal.isPascal()) { + CharTy = Context.UnsignedCharTy; + } + QualType CharTyConst = CharTy; // A C++ string literal has a const-qualified element type (C++ 2.13.4p1). if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings) - StrTy.addConst(); + CharTyConst.addConst(); // Get an array type for the string, according to C99 6.4.5. This includes // the nul terminator character as well as the string length for pascal // strings. - StrTy = Context.getConstantArrayType(StrTy, + QualType StrTy = Context.getConstantArrayType(CharTyConst, llvm::APInt(32, Literal.GetNumStringChars()+1), - ArrayType::Normal, 0); + ArrayType::Normal, 0); // Pass &StringTokLocs[0], StringTokLocs.size() to factory! StringLiteral *Lit = StringLiteral::Create(Context, Literal.GetString(), @@ -1538,12 +1537,57 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks, // C++11 [lex.ext]p5: The literal L is treated as a call of the form // operator "" X (str, len) QualType SizeType = Context.getSizeType(); - llvm::APInt Len(Context.getIntWidth(SizeType), Literal.GetNumStringChars()); - IntegerLiteral *LenArg = IntegerLiteral::Create(Context, Len, SizeType, - StringTokLocs[0]); - Expr *Args[] = { Lit, LenArg }; - return BuildCookedLiteralOperatorCall(*this, UDLScope, UDSuffix, UDSuffixLoc, - Args, StringTokLocs.back()); + + DeclarationName OpName = + Context.DeclarationNames.getCXXLiteralOperatorName(UDSuffix); + DeclarationNameInfo OpNameInfo(OpName, UDSuffixLoc); + OpNameInfo.setCXXLiteralOperatorNameLoc(UDSuffixLoc); + + QualType ArgTy[] = { + Context.getArrayDecayedType(StrTy), SizeType + }; + + LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName); + switch (LookupLiteralOperator(UDLScope, R, ArgTy, + /*AllowRaw*/false, /*AllowTemplate*/false, + /*AllowStringTemplate*/true)) { + + case LOLR_Cooked: { + llvm::APInt Len(Context.getIntWidth(SizeType), Literal.GetNumStringChars()); + IntegerLiteral *LenArg = IntegerLiteral::Create(Context, Len, SizeType, + StringTokLocs[0]); + Expr *Args[] = { Lit, LenArg }; + + return BuildLiteralOperatorCall(R, OpNameInfo, Args, StringTokLocs.back()); + } + + case LOLR_StringTemplate: { + TemplateArgumentListInfo ExplicitArgs; + + unsigned CharBits = Context.getIntWidth(CharTy); + bool CharIsUnsigned = CharTy->isUnsignedIntegerType(); + llvm::APSInt Value(CharBits, CharIsUnsigned); + + TemplateArgument TypeArg(CharTy); + TemplateArgumentLocInfo TypeArgInfo(Context.getTrivialTypeSourceInfo(CharTy)); + ExplicitArgs.addArgument(TemplateArgumentLoc(TypeArg, TypeArgInfo)); + + for (unsigned I = 0, N = Lit->getLength(); I != N; ++I) { + Value = Lit->getCodeUnit(I); + TemplateArgument Arg(Context, Value, CharTy); + TemplateArgumentLocInfo ArgInfo; + ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo)); + } + return BuildLiteralOperatorCall(R, OpNameInfo, None, StringTokLocs.back(), + &ExplicitArgs); + } + case LOLR_Raw: + case LOLR_Template: + llvm_unreachable("unexpected literal operator lookup result"); + case LOLR_Error: + return ExprError(); + } + llvm_unreachable("unexpected literal operator lookup result"); } ExprResult @@ -2942,11 +2986,14 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { DeclarationNameInfo OpNameInfo(OpName, UDSuffixLoc); OpNameInfo.setCXXLiteralOperatorNameLoc(UDSuffixLoc); + SourceLocation TokLoc = Tok.getLocation(); + // Perform literal operator lookup to determine if we're building a raw // literal or a cooked one. LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName); switch (LookupLiteralOperator(UDLScope, R, CookedTy, - /*AllowRawAndTemplate*/true)) { + /*AllowRaw*/true, /*AllowTemplate*/true, + /*AllowStringTemplate*/false)) { case LOLR_Error: return ExprError(); @@ -2961,15 +3008,13 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { Lit = IntegerLiteral::Create(Context, ResultVal, CookedTy, Tok.getLocation()); } - return BuildLiteralOperatorCall(R, OpNameInfo, Lit, - Tok.getLocation()); + return BuildLiteralOperatorCall(R, OpNameInfo, Lit, TokLoc); } case LOLR_Raw: { // C++11 [lit.ext]p3, p4: If S contains a raw literal operator, the // literal is treated as a call of the form // operator "" X ("n") - SourceLocation TokLoc = Tok.getLocation(); unsigned Length = Literal.getUDSuffixOffset(); QualType StrTy = Context.getConstantArrayType( Context.CharTy.withConst(), llvm::APInt(32, Length + 1), @@ -2980,7 +3025,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { return BuildLiteralOperatorCall(R, OpNameInfo, Lit, TokLoc); } - case LOLR_Template: + case LOLR_Template: { // C++11 [lit.ext]p3, p4: Otherwise (S contains a literal operator // template), L is treated as a call fo the form // operator "" X <'c1', 'c2', ... 'ck'>() @@ -2995,11 +3040,12 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { TemplateArgumentLocInfo ArgInfo; ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo)); } - return BuildLiteralOperatorCall(R, OpNameInfo, None, Tok.getLocation(), + return BuildLiteralOperatorCall(R, OpNameInfo, None, TokLoc, &ExplicitArgs); } - - llvm_unreachable("unexpected literal operator lookup result"); + case LOLR_StringTemplate: + llvm_unreachable("unexpected literal operator lookup result"); + } } Expr *Res; diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 44d457e1e1d406bbac8b76a9d21082e5bd998fc7..2b7c0b1cf867ad7220858a5db8a35432741f5f55 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -2711,7 +2711,8 @@ CXXDestructorDecl *Sema::LookupDestructor(CXXRecordDecl *Class) { Sema::LiteralOperatorLookupResult Sema::LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef<QualType> ArgTys, - bool AllowRawAndTemplate) { + bool AllowRaw, bool AllowTemplate, + bool AllowStringTemplate) { LookupName(R, S); assert(R.getResultKind() != LookupResult::Ambiguous && "literal operator lookup can't be ambiguous"); @@ -2719,8 +2720,9 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, // Filter the lookup results appropriately. LookupResult::Filter F = R.makeFilter(); - bool FoundTemplate = false; bool FoundRaw = false; + bool FoundTemplate = false; + bool FoundStringTemplate = false; bool FoundExactMatch = false; while (F.hasNext()) { @@ -2728,16 +2730,17 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(D)) D = USD->getTargetDecl(); - bool IsTemplate = isa<FunctionTemplateDecl>(D); - bool IsRaw = false; - bool IsExactMatch = false; - // If the declaration we found is invalid, skip it. if (D->isInvalidDecl()) { F.erase(); continue; } + bool IsRaw = false; + bool IsTemplate = false; + bool IsStringTemplate = false; + bool IsExactMatch = false; + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { if (FD->getNumParams() == 1 && FD->getParamDecl(0)->getType()->getAs<PointerType>()) @@ -2753,19 +2756,31 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, } } } + if (FunctionTemplateDecl *FD = dyn_cast<FunctionTemplateDecl>(D)) { + TemplateParameterList *Params = FD->getTemplateParameters(); + if (Params->size() == 1) + IsTemplate = true; + else + IsStringTemplate = true; + } if (IsExactMatch) { FoundExactMatch = true; - AllowRawAndTemplate = false; - if (FoundRaw || FoundTemplate) { + AllowRaw = false; + AllowTemplate = false; + AllowStringTemplate = false; + if (FoundRaw || FoundTemplate || FoundStringTemplate) { // Go through again and remove the raw and template decls we've // already found. F.restart(); - FoundRaw = FoundTemplate = false; + FoundRaw = FoundTemplate = FoundStringTemplate = false; } - } else if (AllowRawAndTemplate && (IsTemplate || IsRaw)) { - FoundTemplate |= IsTemplate; - FoundRaw |= IsRaw; + } else if (AllowRaw && IsRaw) { + FoundRaw = true; + } else if (AllowTemplate && IsTemplate) { + FoundTemplate = true; + } else if (AllowStringTemplate && IsStringTemplate) { + FoundStringTemplate = true; } else { F.erase(); } @@ -2800,10 +2815,14 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, if (FoundTemplate) return LOLR_Template; + if (FoundStringTemplate) + return LOLR_StringTemplate; + // Didn't find anything we could use. Diag(R.getNameLoc(), diag::err_ovl_no_viable_literal_operator) << R.getLookupName() << (int)ArgTys.size() << ArgTys[0] - << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRawAndTemplate; + << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRaw + << (AllowTemplate || AllowStringTemplate); return LOLR_Error; } diff --git a/test/CXX/lex/lex.literal/lex.ext/p11.cpp b/test/CXX/lex/lex.literal/lex.ext/p11.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b5fcf4b609b65395c074c4468067af88005605c --- /dev/null +++ b/test/CXX/lex/lex.literal/lex.ext/p11.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s + +using size_t = decltype(sizeof(int)); + +template<typename T, typename U> struct same_type; +template<typename T> struct same_type<T, T> {}; +template<typename T> using X = T; +template<typename CharT, X<CharT>...> +int operator "" _x(); // expected-warning {{string literal operator templates are a GNU extension}} +template<char...> +double operator "" _x(); + +auto a="string"_x; +auto b=42_x; +same_type<decltype(a), int> test_a; +same_type<decltype(b), double> test_b; + +char operator "" _x(const char *begin, size_t size); +auto c="string"_x; +auto d=L"string"_x; +same_type<decltype(c), char> test_c; +same_type<decltype(d), int> test_d; diff --git a/test/CXX/lex/lex.literal/lex.ext/p12.cpp b/test/CXX/lex/lex.literal/lex.ext/p12.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dad4680939848baf9f2e70bd506880f659f62caf --- /dev/null +++ b/test/CXX/lex/lex.literal/lex.ext/p12.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -std=gnu++11 -verify %s + +template<typename T, T... cs> struct check; // expected-note {{template is declared here}} expected-note {{template is declared here}} +template<> +struct check<char, 34, -47, -126, -48, -75, -47, -127, -47, -126, 32, -16, -112, -128, -128>{}; +template<> +struct check<char16_t, 34, 1090, 1077, 1089, 1090, 32, 55296, 56320>{}; +template<> +struct check<char32_t, 34, 1090, 1077, 1089, 1090, 32, 65536>{}; +template<typename T, T... str> int operator""_x() { // #1 expected-warning {{string literal operator templates are a GNU extension}} + check<T, str...> chars; // expected-error {{implicit instantiation of undefined template 'check<char, 't', 'e', 's', 't'>'}} expected-error {{implicit instantiation of undefined template 'check<char32_t, 34, 1090, 1077, 1089, 1090, 95, 65536>'}} + return 1; +} +void *operator""_x(const char*); // #2 +void *a = 123_x; // ok, calls #2 +int b = u8"\"теÑÑ‚ ð€€"_x; // ok, calls #1 +int c = u8R"("теÑÑ‚ ð€€)"_x; // ok, calls #1 +int d = "test"_x; // expected-note {{in instantiation of function template specialization 'operator "" _x<char, 't', 'e', 's', 't'>' requested here}} +int e = uR"("теÑÑ‚ ð€€)"_x; +int f = UR"("теÑÑ‚ ð€€)"_x; +int g = UR"("теÑÑ‚_ð€€)"_x; // expected-note {{in instantiation of function template specialization 'operator "" _x<char32_t, 34, 1090, 1077, 1089, 1090, 95, 65536>' requested here}} diff --git a/test/CXX/lex/lex.literal/lex.ext/p5.cpp b/test/CXX/lex/lex.literal/lex.ext/p5.cpp index 06c091d8acae19e5ba67b6440a5aaef73fdf03ab..ff7c3b345ce8fc9ea99cca818193b1d1cc74ae92 100644 --- a/test/CXX/lex/lex.literal/lex.ext/p5.cpp +++ b/test/CXX/lex/lex.literal/lex.ext/p5.cpp @@ -6,7 +6,7 @@ int &operator "" _x1 (const char *); double &operator "" _x1 (const char *, size_t); double &i1 = "foo"_x1; double &i2 = u8"foo"_x1; -double &i3 = L"foo"_x1; // expected-error {{no matching literal operator}} +double &i3 = L"foo"_x1; // expected-error {{no matching literal operator for call to 'operator "" _x1' with arguments of types 'const wchar_t *' and 'unsigned long'}} char &operator "" _x1(const wchar_t *, size_t); char &i4 = L"foo"_x1; // ok diff --git a/test/CXX/lex/lex.literal/lex.ext/p7.cpp b/test/CXX/lex/lex.literal/lex.ext/p7.cpp index 79c9394a96ba3437c3939fbc64b7f5031c271386..15321eb939cec57cc7384db4febea8113c65103f 100644 --- a/test/CXX/lex/lex.literal/lex.ext/p7.cpp +++ b/test/CXX/lex/lex.literal/lex.ext/p7.cpp @@ -17,7 +17,7 @@ int main() { auto v1 = 1.2_w; // calls operator "" _w(1.2L) auto v2 = u"one"_w; // calls operator "" _w(u"one", 3) auto v3 = 12_w; // calls operator "" _w("12") - "two"_w; // expected-error {{no matching literal operator}} + "two"_w; // expected-error {{no matching literal operator for call to 'operator "" _w' with arguments of types 'const char *' and 'unsigned long'}} same_type<decltype(v1), long double> test1; same_type<decltype(v2), std::string> test2;