/// 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,
CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class);
bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id);
- LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R,
- ArrayRef<QualType> ArgTys,
- bool AllowRaw,
- bool AllowTemplate,
- bool AllowStringTemplate,
- bool DiagnoseMissing);
+ LiteralOperatorLookupResult
+ LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef<QualType> 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.
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<DeducedTemplateSpecializationType>()))
+ return false;
} else if (TemplateParams->size() == 2) {
TemplateTypeParmDecl *PmType =
dyn_cast<TemplateTypeParmDecl>(TemplateParams->getParam(0));
// 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.
+ //
+ // C++20 also allows template <SomeClass T> type operator "" name().
if (TpDecl) {
if (FnDecl->param_size() != 0) {
Diag(FnDecl->getLocation(),
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();
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());
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);
&ExplicitArgs);
}
case LOLR_Raw:
- case LOLR_Template:
case LOLR_ErrorNoDiagnostic:
llvm_unreachable("unexpected literal operator lookup result");
case LOLR_Error:
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
return BuildLiteralOperatorCall(R, OpNameInfo, None, TokLoc,
&ExplicitArgs);
}
- case LOLR_StringTemplate:
+ case LOLR_StringTemplatePack:
llvm_unreachable("unexpected literal operator lookup result");
}
}
/// and filter the results to the appropriate set for the given argument types.
Sema::LiteralOperatorLookupResult
Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
- ArrayRef<QualType> ArgTys,
- bool AllowRaw, bool AllowTemplate,
- bool AllowStringTemplate, bool DiagnoseMissing) {
+ ArrayRef<QualType> 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");
// 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();
bool IsRaw = false;
bool IsTemplate = false;
- bool IsStringTemplate = false;
- bool IsExactMatch = false;
+ bool IsStringTemplatePack = false;
+ bool IsCooked = false;
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getNumParams() == 1 &&
FD->getParamDecl(0)->getType()->getAs<PointerType>())
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;
}
}
}
if (FunctionTemplateDecl *FD = dyn_cast<FunctionTemplateDecl>(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<TemplateArgument, 1> 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();
}
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
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;
}
-// 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<int N> 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<S s> constexpr const decltype(s) &operator""_str() { return s; }
+static_assert(__builtin_strcmp("hello world"_str.a, "hello world") == 0);
+
+template<S> 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<S<4>> 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<int N> struct X {
+ static constexpr int size = N;
+ constexpr X(const char (&r)[N]) {}
+};
+template<X x> 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<int N> requires (N == 4)
+struct Y {
+ constexpr Y(const char (&r)[N]) {}
+};
+template<Y> 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