diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 18f115003f389d..ed409b93545ec2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3889,7 +3889,7 @@ class Sema final { /// 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 + LOLR_StringTemplatePack, }; SpecialMemberOverloadResult LookupSpecialMember(CXXRecordDecl *D, @@ -3997,12 +3997,11 @@ class Sema final { CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id); - LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R, - ArrayRef ArgTys, - bool AllowRaw, - bool AllowTemplate, - bool AllowStringTemplate, - bool DiagnoseMissing); + LiteralOperatorLookupResult + LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef ArgTys, + bool AllowRaw, bool AllowTemplate, + bool AllowStringTemplate, bool DiagnoseMissing, + StringLiteral *StringLit = nullptr); bool isKnownName(StringRef name); /// Status of the function emission on the CUDA/HIP/OpenMP host/device attrs. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 6589b37338e954..bc119725e4a0aa 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -15522,6 +15522,18 @@ checkLiteralOperatorTemplateParameterList(Sema &SemaRef, SemaRef.Context.hasSameType(PmDecl->getType(), SemaRef.Context.CharTy)) return false; + // C++20 [over.literal]p5: + // A string literal operator template is a literal operator template + // whose template-parameter-list comprises a single non-type + // template-parameter of class type. + // + // As a DR resolution, we also allow placeholders for deduced class + // template specializations. + if (SemaRef.getLangOpts().CPlusPlus20 && + !PmDecl->isTemplateParameterPack() && + (PmDecl->getType()->isRecordType() || + PmDecl->getType()->getAs())) + return false; } else if (TemplateParams->size() == 2) { TemplateTypeParmDecl *PmType = dyn_cast(TemplateParams->getParam(0)); @@ -15578,6 +15590,8 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) { // template type operator "" name() and // template type operator "" name() are the only valid // template signatures, and the only valid signatures with no parameters. + // + // C++20 also allows template type operator "" name(). if (TpDecl) { if (FnDecl->param_size() != 0) { Diag(FnDecl->getLocation(), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 6bc838a1acd5d5..13db8362e065fd 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1758,7 +1758,7 @@ static ExprResult BuildCookedLiteralOperatorCall(Sema &S, Scope *Scope, LookupResult R(S, OpName, UDSuffixLoc, Sema::LookupOrdinaryName); if (S.LookupLiteralOperator(Scope, R, llvm::makeArrayRef(ArgTy, Args.size()), /*AllowRaw*/ false, /*AllowTemplate*/ false, - /*AllowStringTemplate*/ false, + /*AllowStringTemplatePack*/ false, /*DiagnoseMissing*/ true) == Sema::LOLR_Error) return ExprError(); @@ -1863,9 +1863,9 @@ Sema::ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope) { LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName); switch (LookupLiteralOperator(UDLScope, R, ArgTy, - /*AllowRaw*/ false, /*AllowTemplate*/ false, - /*AllowStringTemplate*/ true, - /*DiagnoseMissing*/ true)) { + /*AllowRaw*/ false, /*AllowTemplate*/ true, + /*AllowStringTemplatePack*/ true, + /*DiagnoseMissing*/ true, Lit)) { case LOLR_Cooked: { llvm::APInt Len(Context.getIntWidth(SizeType), Literal.GetNumStringChars()); @@ -1876,7 +1876,16 @@ Sema::ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope) { return BuildLiteralOperatorCall(R, OpNameInfo, Args, StringTokLocs.back()); } - case LOLR_StringTemplate: { + case LOLR_Template: { + TemplateArgumentListInfo ExplicitArgs; + TemplateArgument Arg(Lit); + TemplateArgumentLocInfo ArgInfo(Lit); + ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo)); + return BuildLiteralOperatorCall(R, OpNameInfo, None, StringTokLocs.back(), + &ExplicitArgs); + } + + case LOLR_StringTemplatePack: { TemplateArgumentListInfo ExplicitArgs; unsigned CharBits = Context.getIntWidth(CharTy); @@ -1897,7 +1906,6 @@ Sema::ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope) { &ExplicitArgs); } case LOLR_Raw: - case LOLR_Template: case LOLR_ErrorNoDiagnostic: llvm_unreachable("unexpected literal operator lookup result"); case LOLR_Error: @@ -3641,7 +3649,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName); switch (LookupLiteralOperator(UDLScope, R, CookedTy, /*AllowRaw*/ true, /*AllowTemplate*/ true, - /*AllowStringTemplate*/ false, + /*AllowStringTemplatePack*/ false, /*DiagnoseMissing*/ !Literal.isImaginary)) { case LOLR_ErrorNoDiagnostic: // Lookup failure for imaginary constants isn't fatal, there's still the @@ -3696,7 +3704,7 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) { return BuildLiteralOperatorCall(R, OpNameInfo, None, TokLoc, &ExplicitArgs); } - case LOLR_StringTemplate: + case LOLR_StringTemplatePack: llvm_unreachable("unexpected literal operator lookup result"); } } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index cf3ae7ae5d0535..eb6ce6fdf0a17b 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3335,9 +3335,9 @@ CXXDestructorDecl *Sema::LookupDestructor(CXXRecordDecl *Class) { /// and filter the results to the appropriate set for the given argument types. Sema::LiteralOperatorLookupResult Sema::LookupLiteralOperator(Scope *S, LookupResult &R, - ArrayRef ArgTys, - bool AllowRaw, bool AllowTemplate, - bool AllowStringTemplate, bool DiagnoseMissing) { + ArrayRef ArgTys, bool AllowRaw, + bool AllowTemplate, bool AllowStringTemplatePack, + bool DiagnoseMissing, StringLiteral *StringLit) { LookupName(R, S); assert(R.getResultKind() != LookupResult::Ambiguous && "literal operator lookup can't be ambiguous"); @@ -3345,10 +3345,11 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, // Filter the lookup results appropriately. LookupResult::Filter F = R.makeFilter(); + bool AllowCooked = true; bool FoundRaw = false; bool FoundTemplate = false; - bool FoundStringTemplate = false; - bool FoundExactMatch = false; + bool FoundStringTemplatePack = false; + bool FoundCooked = false; while (F.hasNext()) { Decl *D = F.next(); @@ -3363,19 +3364,19 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, bool IsRaw = false; bool IsTemplate = false; - bool IsStringTemplate = false; - bool IsExactMatch = false; + bool IsStringTemplatePack = false; + bool IsCooked = false; if (FunctionDecl *FD = dyn_cast(D)) { if (FD->getNumParams() == 1 && FD->getParamDecl(0)->getType()->getAs()) IsRaw = true; else if (FD->getNumParams() == ArgTys.size()) { - IsExactMatch = true; + IsCooked = true; for (unsigned ArgIdx = 0; ArgIdx != ArgTys.size(); ++ArgIdx) { QualType ParamTy = FD->getParamDecl(ArgIdx)->getType(); if (!Context.hasSameUnqualifiedType(ArgTys[ArgIdx], ParamTy)) { - IsExactMatch = false; + IsCooked = false; break; } } @@ -3383,29 +3384,52 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, } if (FunctionTemplateDecl *FD = dyn_cast(D)) { TemplateParameterList *Params = FD->getTemplateParameters(); - if (Params->size() == 1) + if (Params->size() == 1) { IsTemplate = true; - else - IsStringTemplate = true; + + // A string literal template is only considered if the string literal + // is a well-formed template argument for the template parameter. + if (StringLit) { + SFINAETrap Trap(*this); + SmallVector Checked; + TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit); + if (CheckTemplateArgument(Params->getParam(0), Arg, FD, + R.getNameLoc(), R.getNameLoc(), 0, + Checked) || + Trap.hasErrorOccurred()) + IsTemplate = false; + } + } else { + IsStringTemplatePack = true; + } } - if (IsExactMatch) { - FoundExactMatch = true; + if (AllowTemplate && StringLit && IsTemplate) { + FoundTemplate = true; AllowRaw = false; - AllowTemplate = false; - AllowStringTemplate = false; - if (FoundRaw || FoundTemplate || FoundStringTemplate) { + AllowCooked = false; + AllowStringTemplatePack = false; + if (FoundRaw || FoundCooked || FoundStringTemplatePack) { + F.restart(); + FoundRaw = FoundCooked = FoundStringTemplatePack = false; + } + } else if (AllowCooked && IsCooked) { + FoundCooked = true; + AllowRaw = false; + AllowTemplate = StringLit; + AllowStringTemplatePack = false; + if (FoundRaw || FoundTemplate || FoundStringTemplatePack) { // Go through again and remove the raw and template decls we've // already found. F.restart(); - FoundRaw = FoundTemplate = FoundStringTemplate = false; + FoundRaw = FoundTemplate = FoundStringTemplatePack = false; } } else if (AllowRaw && IsRaw) { FoundRaw = true; } else if (AllowTemplate && IsTemplate) { FoundTemplate = true; - } else if (AllowStringTemplate && IsStringTemplate) { - FoundStringTemplate = true; + } else if (AllowStringTemplatePack && IsStringTemplatePack) { + FoundStringTemplatePack = true; } else { F.erase(); } @@ -3413,10 +3437,15 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, F.done(); + // Per C++20 [lex.ext]p5, we prefer the template form over the non-template + // form for string literal operator templates. + if (StringLit && FoundTemplate) + return LOLR_Template; + // C++11 [lex.ext]p3, p4: If S contains a literal operator with a matching // parameter type, that is used in preference to a raw literal operator // or literal operator template. - if (FoundExactMatch) + if (FoundCooked) return LOLR_Cooked; // C++11 [lex.ext]p3, p4: S shall contain a raw literal operator or a literal @@ -3434,15 +3463,15 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, if (FoundTemplate) return LOLR_Template; - if (FoundStringTemplate) - return LOLR_StringTemplate; + if (FoundStringTemplatePack) + return LOLR_StringTemplatePack; // Didn't find anything we could use. if (DiagnoseMissing) { Diag(R.getNameLoc(), diag::err_ovl_no_viable_literal_operator) << R.getLookupName() << (int)ArgTys.size() << ArgTys[0] << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRaw - << (AllowTemplate || AllowStringTemplate); + << (AllowTemplate || AllowStringTemplatePack); return LOLR_Error; } diff --git a/clang/test/CXX/lex/lex.literal/lex.ext/p5.cpp b/clang/test/CXX/lex/lex.literal/lex.ext/p5.cpp index a516168a69b632..aee20545ececfc 100644 --- a/clang/test/CXX/lex/lex.literal/lex.ext/p5.cpp +++ b/clang/test/CXX/lex/lex.literal/lex.ext/p5.cpp @@ -1,20 +1,92 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu using size_t = decltype(sizeof(int)); int &operator "" _x1 (const char *); double &operator "" _x1 (const char *, size_t); double &i1 = "foo"_x1; -double &i2 = u8"foo"_x1; +#if __cplusplus >= 202002L +using char8 = float; +float &operator "" _x1 (const char8_t *, size_t); +#else +using char8 = double; +#endif +char8 &i2 = u8"foo"_x1; 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 double &i5 = R"(foo)"_x1; // ok -double &i6 = u\ +char8 &i6 = u\ 8\ R\ "(foo)"\ _\ x\ 1; // ok + +#if __cplusplus >= 202002L +template struct S { + char a[N]; + constexpr S(const char (&r)[N]) { + __builtin_memcpy(a, r, N); + if (a[0] == 'x') throw "no"; + } + constexpr ~S() { + if (a[0] == 'y') throw "also no"; + } +}; + +// Check the produced contents are correct. +template constexpr const decltype(s) &operator""_str() { return s; } +static_assert(__builtin_strcmp("hello world"_str.a, "hello world") == 0); + +template float &operator""_s(); +void no_fallback() { + "hello"_s; + // FIXME: It'd be useful to explain what candidates were found and why they didn't work. + "xyzzy"_s; // expected-error {{no matching literal operator for call to 'operator""_s' with arguments of types 'const char *' and 'unsigned long', and no matching literal operator template}} + "yello"_s; // expected-error {{no matching literal operator for call to 'operator""_s' with arguments of types 'const char *' and 'unsigned long', and no matching literal operator template}} +} + +double &operator""_s(const char*, size_t); +void f() { + float &a = "foo"_s; + double &b = "xar"_s; + double &c = "yar"_s; +} + +template> float &operator""_t(); +double &operator""_t(const char*, size_t); +void g() { + double &a = "fo"_t; + float &b = "foo"_t; + double &c = "fooo"_t; +} + +template struct X { + static constexpr int size = N; + constexpr X(const char (&r)[N]) {} +}; +template requires (x.size == 4) // expected-note {{because 'X<5>{}.size == 4' (5 == 4) evaluated to false}} +void operator""_x(); // expected-note {{constraints not satisfied}} +void operator""_x(const char*, size_t) = delete; + +template requires (N == 4) +struct Y { + constexpr Y(const char (&r)[N]) {} +}; +template float &operator""_y(); +void operator""_y(const char*, size_t) = delete; // expected-note {{deleted here}} + +void test() { + "foo"_x; + "foo"_y; + + // We only check the template argument itself for validity, not the whole + // call, when deciding whether to use the template or non-template form. + "fooo"_x; // expected-error {{no matching function}} + "fooo"_y; // expected-error {{deleted function}} +} +#endif