[clang] [Clang][C++23] Implement P2448R2: Relaxing some constexpr restrictions (PR #77753)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 11 03:07:14 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Mariya Podchishchaeva (Fznamznon)
<details>
<summary>Changes</summary>
Per https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2448r2.html function/constructor/destructor can be marked `constexpr` even though it never produces a constant expression.
Non-literal types as return types and parameter types of functions marked `constexpr` are also allowed.
Since this is not a DR, the diagnostic messages are still preserved for C++ standards older than C++23.
---
Patch is 88.09 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/77753.diff
36 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+2)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+26-16)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+39-15)
- (modified) clang/test/AST/Interp/cxx23.cpp (+6-18)
- (modified) clang/test/AST/Interp/literals.cpp (+1-1)
- (modified) clang/test/AST/Interp/shifts.cpp (+4-4)
- (modified) clang/test/CXX/basic/basic.types/p10.cpp (+13-13)
- (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp (+4-4)
- (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp (+2-2)
- (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp (+13-14)
- (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp (+10-10)
- (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p5.cpp (+3-3)
- (modified) clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p6.cpp (+1-1)
- (modified) clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp (+3-3)
- (modified) clang/test/CXX/drs/dr13xx.cpp (+11-11)
- (modified) clang/test/CXX/drs/dr14xx.cpp (+3-3)
- (modified) clang/test/CXX/drs/dr16xx.cpp (+6-6)
- (modified) clang/test/CXX/drs/dr6xx.cpp (+12-12)
- (modified) clang/test/CXX/expr/expr.const/p2-0x.cpp (+1-1)
- (modified) clang/test/CXX/expr/expr.const/p5-26.cpp (+2-2)
- (modified) clang/test/CXX/special/class.copy/p13-0x.cpp (+1-1)
- (modified) clang/test/PCH/cxx11-constexpr.cpp (+1-1)
- (modified) clang/test/SemaCXX/builtin_vectorelements.cpp (+1-1)
- (modified) clang/test/SemaCXX/constant-expression-cxx11.cpp (+15-14)
- (modified) clang/test/SemaCXX/constant-expression-cxx14.cpp (+17-16)
- (modified) clang/test/SemaCXX/constant-expression-cxx2b.cpp (+12-6)
- (modified) clang/test/SemaCXX/constexpr-function-recovery-crash.cpp (+2-2)
- (modified) clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp (+2-2)
- (added) clang/test/SemaCXX/cxx23-invalid-constexpr.cpp (+152)
- (modified) clang/test/SemaCXX/cxx2a-consteval.cpp (+2-2)
- (modified) clang/test/SemaCXX/deduced-return-type-cxx14.cpp (+4-4)
- (modified) clang/test/SemaCXX/ms-constexpr-invalid.cpp (+3-3)
- (modified) clang/test/SemaCXX/ms-constexpr.cpp (+1-1)
- (modified) clang/test/SemaCXX/sizeless-1.cpp (+1-1)
- (modified) clang/test/SemaOpenCLCXX/addrspace-constructors.clcpp (+1-1)
- (modified) clang/www/cxx_status.html (+1-8)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a18d36a16b1a9c..23342a6a7256d8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -165,6 +165,8 @@ C++23 Feature Support
- Added a separate warning to warn the use of attributes on lambdas as a C++23 extension
in previous language versions: ``-Wc++23-lambda-attributes``.
+- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a79892e40030a..67409374f26dfa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2765,10 +2765,14 @@ def err_constexpr_tag : Error<
"cannot be marked %sub{select_constexpr_spec_kind}1">;
def err_constexpr_dtor : Error<
"destructor cannot be declared %sub{select_constexpr_spec_kind}0">;
-def err_constexpr_dtor_subobject : Error<
- "destructor cannot be declared %sub{select_constexpr_spec_kind}0 because "
+def ext_constexpr_dtor_subobject : ExtWarn<
+ "destructor cannot be declared %sub{select_constexpr_spec_kind}0 before C++23 because "
"%select{data member %2|base class %3}1 does not have a "
- "constexpr destructor">;
+ "constexpr destructor">, InGroup<CXX23>, DefaultError;
+def warn_cxx23_compat_constexpr_dtor_subobject : ExtWarn<
+ "%sub{select_constexpr_spec_kind}0 destructor is incompatible with C++ standards before C++23 because "
+ "%select{data member %2|base class %3}1 does not have a "
+ "constexpr destructor">, InGroup<CXXPre23Compat>, DefaultIgnore;
def note_constexpr_dtor_subobject : Note<
"%select{data member %1|base class %2}0 declared here">;
def err_constexpr_wrong_decl_kind : Error<
@@ -2800,11 +2804,14 @@ def note_non_literal_incomplete : Note<
def note_non_literal_virtual_base : Note<"%select{struct|interface|class}0 "
"with virtual base %plural{1:class|:classes}1 is not a literal type">;
def note_constexpr_virtual_base_here : Note<"virtual base class declared here">;
-def err_constexpr_non_literal_return : Error<
- "%select{constexpr|consteval}0 function's return type %1 is not a literal type">;
-def err_constexpr_non_literal_param : Error<
- "%select{constexpr|consteval}2 %select{function|constructor}1's %ordinal0 parameter type %3 is "
- "not a literal type">;
+def ext_constexpr_non_literal_return : ExtWarn<
+ "%select{constexpr|consteval}0 function with non-literal return type %1 is a C++23 extension">, InGroup<CXX23>, DefaultError;
+def warn_cxx23_compat_constexpr_non_literal_return : Warning<
+ "%select{constexpr|consteval}0 function with non-literal return type %1 is incompatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
+def ext_constexpr_non_literal_param : ExtWarn<
+ "%select{constexpr|consteval}2 %select{function|constructor}1 with %ordinal0 non-literal parameter type %3 is a C++23 extension">, InGroup<CXX23>, DefaultError;
+def warn_cxx23_compat_constexpr_non_literal_param : Warning<
+ "%select{constexpr|consteval}2 %select{function|constructor}1 with %ordinal0 non-literal parameter type %3 is not compatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
def err_constexpr_body_invalid_stmt : Error<
"statement not allowed in %select{constexpr|consteval}1 %select{function|constructor}0">;
def ext_constexpr_body_invalid_stmt : ExtWarn<
@@ -2865,8 +2872,11 @@ def warn_cxx17_compat_constexpr_local_var_no_init : Warning<
"is incompatible with C++ standards before C++20">,
InGroup<CXXPre20Compat>, DefaultIgnore;
def ext_constexpr_function_never_constant_expr : ExtWarn<
- "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a "
- "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
+ "%select{constexpr|consteval}1 %select{function|constructor}0 that never produces a "
+ "constant expression is a C++23 extension">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
+def warn_cxx23_compat_constexpr_function_never_constant_expr : Warning<
+ "%select{constexpr|consteval}1 %select{function|constructor}0 that never produces a "
+ "constant expression is incompatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
def err_attr_cond_never_constant_expr : Error<
"%0 attribute expression never produces a constant expression">;
def err_diagnose_if_invalid_diagnostic_type : Error<
@@ -9539,14 +9549,14 @@ def err_defaulted_special_member_copy_const_param : Error<
def err_defaulted_copy_assign_not_ref : Error<
"the parameter for an explicitly-defaulted copy assignment operator must be an "
"lvalue reference type">;
-def err_incorrect_defaulted_constexpr : Error<
- "defaulted definition of %sub{select_special_member_kind}0 "
- "is not constexpr">;
+def ext_incorrect_defaulted_constexpr : ExtWarn<
+ "defaulted definition of %sub{select_special_member_kind}0 that marked %select{constexpr|consteval}1 "
+ "but never produces a constant expression is a C++23 extension">, InGroup<CXX23>, DefaultError;
+def warn_cxx23_compat_incorrect_defaulted_constexpr : Warning<
+ "defaulted definition of %sub{select_special_member_kind}0 that marked %select{constexpr|consteval}1 "
+ "but never produces a constant expression is incompatible with C++ standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
def err_incorrect_defaulted_constexpr_with_vb: Error<
"%sub{select_special_member_kind}0 cannot be 'constexpr' in a class with virtual base class">;
-def err_incorrect_defaulted_consteval : Error<
- "defaulted declaration of %sub{select_special_member_kind}0 "
- "cannot be consteval because implicit definition is not constexpr">;
def warn_defaulted_method_deleted : Warning<
"explicitly defaulted %sub{select_special_member_kind}0 is implicitly "
"deleted">, InGroup<DefaultedFunctionDeleted>;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 36e53c684ac4dc..aa49dc73e157bf 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1722,12 +1722,19 @@ static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
return true;
if (Kind == Sema::CheckConstexprKind::Diagnose) {
- SemaRef.Diag(DD->getLocation(), diag::err_constexpr_dtor_subobject)
+ SemaRef.Diag(DD->getLocation(),
+ SemaRef.getLangOpts().CPlusPlus23
+ ? diag::warn_cxx23_compat_constexpr_dtor_subobject
+ : diag::ext_constexpr_dtor_subobject)
<< static_cast<int>(DD->getConstexprKind()) << !FD
<< (FD ? FD->getDeclName() : DeclarationName()) << T;
SemaRef.Diag(Loc, diag::note_constexpr_dtor_subobject)
<< !FD << (FD ? FD->getDeclName() : DeclarationName()) << T;
}
+
+ if (SemaRef.getLangOpts().CPlusPlus23)
+ return true;
+
return false;
};
@@ -1754,11 +1761,17 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
const ParmVarDecl *PD = FD->getParamDecl(ArgIndex);
assert(PD && "null in a parameter list");
SourceLocation ParamLoc = PD->getLocation();
- if (CheckLiteralType(SemaRef, Kind, ParamLoc, *i,
- diag::err_constexpr_non_literal_param, ArgIndex + 1,
- PD->getSourceRange(), isa<CXXConstructorDecl>(FD),
- FD->isConsteval()))
+ if (CheckLiteralType(
+ SemaRef, Kind, ParamLoc, *i,
+ SemaRef.getLangOpts().CPlusPlus23
+ ? diag::warn_cxx23_compat_constexpr_non_literal_param
+ : diag::ext_constexpr_non_literal_param,
+ ArgIndex + 1, PD->getSourceRange(), isa<CXXConstructorDecl>(FD),
+ FD->isConsteval())) {
+ if (SemaRef.getLangOpts().CPlusPlus23)
+ return true;
return false;
+ }
}
return true;
}
@@ -1767,10 +1780,16 @@ static bool CheckConstexprParameterTypes(Sema &SemaRef,
/// true. If not, produce a suitable diagnostic and return false.
static bool CheckConstexprReturnType(Sema &SemaRef, const FunctionDecl *FD,
Sema::CheckConstexprKind Kind) {
- if (CheckLiteralType(SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
- diag::err_constexpr_non_literal_return,
- FD->isConsteval()))
+ if (CheckLiteralType(
+ SemaRef, Kind, FD->getLocation(), FD->getReturnType(),
+ SemaRef.getLangOpts().CPlusPlus23
+ ? diag::warn_cxx23_compat_constexpr_non_literal_return
+ : diag::ext_constexpr_non_literal_return,
+ FD->isConsteval())) {
+ if (SemaRef.getLangOpts().CPlusPlus23)
+ return true;
return false;
+ }
return true;
}
@@ -2458,8 +2477,11 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
SmallVector<PartialDiagnosticAt, 8> Diags;
if (Kind == Sema::CheckConstexprKind::Diagnose &&
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
- SemaRef.Diag(Dcl->getLocation(),
- diag::ext_constexpr_function_never_constant_expr)
+ SemaRef.Diag(
+ Dcl->getLocation(),
+ SemaRef.getLangOpts().CPlusPlus23
+ ? diag::warn_cxx23_compat_constexpr_function_never_constant_expr
+ : diag::ext_constexpr_function_never_constant_expr)
<< isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
<< Dcl->getNameInfo().getSourceRange();
for (size_t I = 0, N = Diags.size(); I != N; ++I)
@@ -7852,13 +7874,15 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
for (const auto &I : RD->vbases())
Diag(I.getBeginLoc(), diag::note_constexpr_virtual_base_here);
} else {
- Diag(MD->getBeginLoc(), MD->isConsteval()
- ? diag::err_incorrect_defaulted_consteval
- : diag::err_incorrect_defaulted_constexpr)
- << CSM;
+ Diag(MD->getBeginLoc(),
+ getLangOpts().CPlusPlus23
+ ? diag::warn_cxx23_compat_incorrect_defaulted_constexpr
+ : diag::ext_incorrect_defaulted_constexpr)
+ << CSM << MD->isConsteval();
}
// FIXME: Explain why the special member can't be constexpr.
- HadError = true;
+ if (!getLangOpts().CPlusPlus23)
+ HadError = true;
}
if (First) {
diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp
index bd1cf186d519c5..6ef7df9662e6a4 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -6,57 +6,45 @@
/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.
-constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+constexpr int f(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
- // ref23-note {{control flows through the definition of a static variable}} \
// expected20-warning {{is a C++23 extension}}
return m;
}
-constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+constexpr int g(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
- // ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}}
return m;
}
-constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+constexpr int c_thread_local(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
static _Thread_local int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
- // ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}}
return m;
}
-constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
static __thread int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
- // ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}}
return m;
}
-constexpr int h(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+constexpr int h(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
- // ref23-note {{control flows through the definition of a static variable}} \
// expected20-warning {{is a C++23 extension}}
return &m - &m;
}
-constexpr int i(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+constexpr int i(int n) { // ref20-error {{constexpr function that never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
- // ref23-note {{control flows through the definition of a thread_local variable}} \
// expected20-warning {{is a C++23 extension}}
return &m - &m;
}
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index 61825bc11438f6..ab09280e883fba 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -275,7 +275,7 @@ namespace SizeOf {
#if __cplusplus >= 202002L
/// FIXME: The following code should be accepted.
- consteval int foo(int n) { // ref-error {{consteval function never produces a constant expression}}
+ consteval int foo(int n) { // ref-error {{consteval function that never produces a constant expression}}
return sizeof(int[n]); // ref-note 3{{not valid in a constant expression}}
}
constinit int var = foo(5); // ref-error {{not a constant expression}} \
diff --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp
index cf71e7145c2742..f8fa1b5d095da0 100644
--- a/clang/test/AST/Interp/shifts.cpp
+++ b/clang/test/AST/Interp/shifts.cpp
@@ -7,10 +7,10 @@
namespace shifts {
- constexpr void test() { // ref-error {{constexpr function never produces a constant expression}} \
- // ref-cxx17-error {{constexpr function never produces a constant expression}} \
- // expected-error {{constexpr function never produces a constant expression}} \
- // cxx17-error {{constexpr function never produces a constant expression}} \
+ constexpr void test() { // ref-error {{constexpr function that never produces a constant expression}} \
+ // ref-cxx17-error {{constexpr function that never produces a constant expression}} \
+ // expected-error {{constexpr function that never produces a constant expression}} \
+ // cxx17-error {{constexpr function that never produces a constant expression}} \
char c; // cxx17-warning {{uninitialized variable}} \
// ref-cxx17-warning {{uninitialized variable}}
diff --git a/clang/test/CXX/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp
index a543f248e53711..19e099d5077de7 100644
--- a/clang/test/CXX/basic/basic.types/p10.cpp
+++ b/clang/test/CXX/basic/basic.types/p10.cpp
@@ -8,7 +8,7 @@ struct NonLiteral { NonLiteral(); };
// [C++1y] - void
constexpr void f() {}
#ifndef CXX1Y
-// expected-error at -2 {{'void' is not a literal type}}
+// expected-error at -2 {{constexpr function with non-literal return type 'void' is a C++23 extension}}
#endif
// - a scalar type
@@ -40,12 +40,12 @@ constexpr ClassTemp<int> classtemplate2[] = {};
struct UserProvDtor {
~UserProvDtor(); // expected-note {{has a user-provided destructor}}
};
-constexpr int f(UserProvDtor) { return 0; } // expected-error {{'UserProvDtor' is not a literal type}}
+constexpr int f(UserProvDtor) { return 0; } // expected-error {{non-literal parameter type 'UserProvDtor'}}
struct NonTrivDtor {
constexpr NonTrivDtor();
virtual ~NonTrivDtor() = default; // expected-note {{has a non-trivial destructor}} expected-note {{because it is virtual}}
};
-constexpr int f(NonTrivDtor) { return 0; } // expected-error {{'NonTrivDtor' is not a literal type}}
+constexpr int f(NonTrivDtor) { return 0; } // expected-error {{non-literal parameter type 'NonTrivDtor'}}
struct NonTrivDtorBase {
~NonTrivDtorBase();
};
@@ -53,7 +53,7 @@ template<typename T>
struct DerivedFromNonTrivDtor : T { // expected-note {{'DerivedFromNonTrivDtor<NonTrivDtorBase>' is not literal because it has base class 'NonTrivDtorBase' of non-literal type}}
constexpr DerivedFromNonTrivDtor();
};
-constexpr int f(DerivedFromNonTrivDtor<NonTrivDtorBase>) { return 0; } // expected-error {{constexpr function's 1st parameter type 'DerivedFromNonTrivDtor<NonTrivDtorBase>' is not a literal type}}
+constexpr int f(DerivedFromNonTrivDtor<NonTrivDtorBase>) { return 0; } // expected-error {{constexpr function with 1st non-literal parameter type 'DerivedFromNonTrivDtor<NonTrivDtorBase>' is a C++23 extension}}
struct TrivDtor {
constexpr TrivDtor();
};
@@ -77,11 +77,11 @@ struct CtorTemplate {
struct CopyCtorOnly { // expected-note {{'CopyCtorOnly' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors}}
constexpr CopyCtorOnly(CopyCtorOnly&);
};
-constexpr int f(CopyCtorOnly) { return 0; } // expected-error {{'CopyCtorOnly' is not a literal type}}
+constexpr int f(CopyCtorOnly) { return 0; } // expected-error {{non-literal parameter type 'CopyCtorOnly'}}
struct MoveCtorOnly { // expected-note {{no constexpr constructors other than copy or move constructors}}
constexpr MoveCtorOnly(MoveCtorOnly&&);
};
-constexpr int f(MoveCtorOnly) { return 0; } // expected-error {{'MoveCtorOnly' is not a literal type}}
+constexpr int f(MoveCtorOnly) { return 0; } // expected-error {{non-literal parameter type 'MoveCtorOnly'}}
template<typename T>
struct CtorArg {
constexpr CtorArg(T);
@@ -97,7 +97,7 @@ struct Derived : HasVBase {
template<typename T> struct DerivedFromVBase : T { // expected-note {{struct with virtual base class is not a literal type}}
constexpr DerivedFromVBase();
};
-constexpr int f(De...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/77753
More information about the cfe-commits
mailing list