forked from OSchip/llvm-project
[c++20] For P0732R2: Support string literal operator templates.
This commit is contained in:
parent
a222d832d5
commit
f81f09ba89
|
@ -3889,7 +3889,7 @@ public:
|
|||
/// 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 @@ public:
|
|||
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.
|
||||
|
|
|
@ -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<DeducedTemplateSpecializationType>()))
|
||||
return false;
|
||||
} else if (TemplateParams->size() == 2) {
|
||||
TemplateTypeParmDecl *PmType =
|
||||
dyn_cast<TemplateTypeParmDecl>(TemplateParams->getParam(0));
|
||||
|
@ -15578,6 +15590,8 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) {
|
|||
// 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(),
|
||||
|
|
|
@ -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<Token> 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<Token> 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<Token> 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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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");
|
||||
|
@ -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<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;
|
||||
}
|
||||
}
|
||||
|
@ -3383,29 +3384,52 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
|
|||
}
|
||||
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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<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
|
||||
|
|
Loading…
Reference in New Issue