diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 988765972a36e..31e7f02dd4305 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -3243,8 +3243,7 @@ bool ByteCodeExprGen::VisitDeclRefExpr(const DeclRefExpr *E) { // This happens in C. if (!Ctx.getLangOpts().CPlusPlus) { if (const auto *VD = dyn_cast(D); - VD && VD->hasGlobalStorage() && VD->getAnyInitializer() && - VD->getType().isConstQualified()) { + VD && VD->getAnyInitializer() && VD->getType().isConstQualified()) { if (!this->visitVarDecl(VD)) return false; // Retry. diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index 7396db2294366..6068b1a5680c8 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -44,7 +44,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { assert(Stk.empty()); ByteCodeExprGen C(*this, *P, Parent, Stk, Result); - auto Res = C.interpretExpr(E); + auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue()); if (Res.isInvalid()) { Stk.clear(); @@ -58,16 +58,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { Stk.clear(); #endif - // Implicit lvalue-to-rvalue conversion. - if (E->isGLValue()) { - std::optional RValueResult = Res.toRValue(); - if (!RValueResult) { - return false; - } - Result = *RValueResult; - } else { - Result = Res.toAPValue(); - } + Result = Res.toAPValue(); return true; } @@ -120,7 +111,8 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, !Res.checkFullyInitialized(C.getState())) return false; - // lvalue-to-rvalue conversion. + // lvalue-to-rvalue conversion. We do this manually here so we can + // examine the result above before converting and returning it. std::optional RValueResult = Res.toRValue(); if (!RValueResult) return false; diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp index c1e4ce3ebb072..f14023a23af9b 100644 --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/clang/lib/AST/Interp/EvalEmitter.cpp @@ -33,7 +33,9 @@ EvalEmitter::~EvalEmitter() { } } -EvaluationResult EvalEmitter::interpretExpr(const Expr *E) { +EvaluationResult EvalEmitter::interpretExpr(const Expr *E, + bool ConvertResultToRValue) { + this->ConvertResultToRValue = ConvertResultToRValue; EvalResult.setSource(E); if (!this->visitExpr(E) && EvalResult.empty()) @@ -119,12 +121,26 @@ template bool EvalEmitter::emitRet(const SourceInfo &Info) { template <> bool EvalEmitter::emitRet(const SourceInfo &Info) { if (!isActive()) return true; - EvalResult.setPointer(S.Stk.pop()); + + const Pointer &Ptr = S.Stk.pop(); + // Implicitly convert lvalue to rvalue, if requested. + if (ConvertResultToRValue) { + if (std::optional V = Ptr.toRValue(Ctx)) { + EvalResult.setValue(*V); + } else { + return false; + } + } else { + EvalResult.setPointer(Ptr); + } + return true; } template <> bool EvalEmitter::emitRet(const SourceInfo &Info) { if (!isActive()) return true; + // Function pointers cannot be converted to rvalues. + assert(!ConvertResultToRValue); EvalResult.setFunctionPointer(S.Stk.pop()); return true; } diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h index deb2ebc4e61fa..8159e489f168e 100644 --- a/clang/lib/AST/Interp/EvalEmitter.h +++ b/clang/lib/AST/Interp/EvalEmitter.h @@ -34,7 +34,8 @@ class EvalEmitter : public SourceMapper { using AddrTy = uintptr_t; using Local = Scope::Local; - EvaluationResult interpretExpr(const Expr *E); + EvaluationResult interpretExpr(const Expr *E, + bool ConvertResultToRValue = false); EvaluationResult interpretDecl(const VarDecl *VD); InterpState &getState() { return S; } @@ -86,6 +87,8 @@ class EvalEmitter : public SourceMapper { InterpState S; /// Location to write the result to. EvaluationResult EvalResult; + /// Whether the result should be converted to an RValue. + bool ConvertResultToRValue = false; /// Temporaries which require storage. llvm::DenseMap> Locals; diff --git a/clang/lib/AST/Interp/EvaluationResult.h b/clang/lib/AST/Interp/EvaluationResult.h index 2b9fc16f1a0ab..52a6c011e39e1 100644 --- a/clang/lib/AST/Interp/EvaluationResult.h +++ b/clang/lib/AST/Interp/EvaluationResult.h @@ -56,8 +56,8 @@ class EvaluationResult final { void setSource(DeclTy D) { Source = D; } void setValue(const APValue &V) { + // V could still be an LValue. assert(empty()); - assert(!V.isLValue()); Value = std::move(V); Kind = RValue; } diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c index 85c195d33a96d..31cd2729f0bc7 100644 --- a/clang/test/AST/Interp/c.c +++ b/clang/test/AST/Interp/c.c @@ -125,3 +125,16 @@ struct XY { int before; struct XX xx, *xp; float* after; } xy[] = { 0, // all-warning {{initializer overrides prior initialization of this subobject}} &xy[2].xx.a, &xy[2].xx, &global_float }; + +void t14(void) { + int array[256] = { 0 }; // expected-note {{array 'array' declared here}} \ + // pedantic-expected-note {{array 'array' declared here}} \ + // ref-note {{array 'array' declared here}} \ + // pedantic-ref-note {{array 'array' declared here}} + const char b = -1; + int val = array[b]; // expected-warning {{array index -1 is before the beginning of the array}} \ + // pedantic-expected-warning {{array index -1 is before the beginning of the array}} \ + // ref-warning {{array index -1 is before the beginning of the array}} \ + // pedantic-ref-warning {{array index -1 is before the beginning of the array}} + +} diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp index 34a832c794c75..320691336bdd9 100644 --- a/clang/test/AST/Interp/functions.cpp +++ b/clang/test/AST/Interp/functions.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s -// RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify %s -// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify %s -// RUN: %clang_cc1 -verify=ref %s -// RUN: %clang_cc1 -std=c++14 -verify=ref %s -// RUN: %clang_cc1 -std=c++20 -verify=ref %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -verify=ref,both %s +// RUN: %clang_cc1 -std=c++14 -verify=ref,both %s +// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s constexpr void doNothing() {} constexpr int gimme5() { @@ -23,16 +23,13 @@ static_assert(!identity(false), ""); template constexpr bool sameSize() { - static_assert(sizeof(A) == sizeof(B), ""); // expected-error {{static assertion failed}} \ - // ref-error {{static assertion failed}} \ - // expected-note {{evaluates to}} \ - // ref-note {{evaluates to}} + static_assert(sizeof(A) == sizeof(B), ""); // both-error {{static assertion failed}} \ + // both-note {{evaluates to}} return true; } static_assert(sameSize(), ""); static_assert(sameSize(), ""); -static_assert(sameSize(), ""); // expected-note {{in instantiation of function template specialization}} \ - // ref-note {{in instantiation of function template specialization}} +static_assert(sameSize(), ""); // both-note {{in instantiation of function template specialization}} constexpr auto add(int a, int b) -> int { @@ -92,12 +89,9 @@ static_assert(getNum<-2>() == -2, ""); static_assert(getNum<10>() == 10, ""); static_assert(getNum() == 5, ""); -constexpr int f(); // expected-note {{declared here}} \ - // ref-note {{declared here}} -static_assert(f() == 5, ""); // expected-error {{not an integral constant expression}} \ - // expected-note {{undefined function 'f'}} \ - // ref-error {{not an integral constant expression}} \ - // ref-note {{undefined function 'f'}} +constexpr int f(); // both-note {{declared here}} +static_assert(f() == 5, ""); // both-error {{not an integral constant expression}} \ + // both-note {{undefined function 'f'}} constexpr int a() { return f(); } @@ -108,17 +102,14 @@ static_assert(a() == 5, ""); constexpr int invalid() { // Invalid expression in visit(). - while(huh) {} // expected-error {{use of undeclared identifier}} \ - // ref-error {{use of undeclared identifier}} - + while(huh) {} // both-error {{use of undeclared identifier}} return 0; } constexpr void invalid2() { int i = 0; // Invalid expression in discard(). - huh(); // expected-error {{use of undeclared identifier}} \ - // ref-error {{use of undeclared identifier}} + huh(); // both-error {{use of undeclared identifier}} } namespace FunctionPointers { @@ -160,8 +151,7 @@ namespace FunctionReturnType { constexpr ptr fun() { return &fun1; } - static_assert(fun() == nullptr, ""); // expected-error {{static assertion failed}} \ - // ref-error {{static assertion failed}} + static_assert(fun() == nullptr, ""); // both-error {{static assertion failed}} constexpr int foo() { int (*f)(int *) = fun(); @@ -188,32 +178,23 @@ namespace FunctionReturnType { constexpr int (*op2)(int, int) = nullptr; static_assert(!op2, ""); - int m() { return 5;} // ref-note {{declared here}} \ - // expected-note {{declared here}} + int m() { return 5;} // both-note {{declared here}} constexpr int (*invalidFnPtr)() = m; - static_assert(invalidFnPtr() == 5, ""); // ref-error {{not an integral constant expression}} \ - // ref-note {{non-constexpr function 'm'}} \ - // expected-error {{not an integral constant expression}} \ - // expected-note {{non-constexpr function 'm'}} + static_assert(invalidFnPtr() == 5, ""); // both-error {{not an integral constant expression}} \ + // both-note {{non-constexpr function 'm'}} } namespace Comparison { void f(), g(); constexpr void (*pf)() = &f, (*pg)() = &g; - constexpr bool u13 = pf < pg; // ref-warning {{ordered comparison of function pointers}} \ - // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{comparison between '&f' and '&g' has unspecified value}} \ - // expected-warning {{ordered comparison of function pointers}} \ - // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{comparison between '&f' and '&g' has unspecified value}} + constexpr bool u13 = pf < pg; // both-warning {{ordered comparison of function pointers}} \ + // both-error {{must be initialized by a constant expression}} \ + // both-note {{comparison between '&f' and '&g' has unspecified value}} - constexpr bool u14 = pf < (void(*)())nullptr; // ref-warning {{ordered comparison of function pointers}} \ - // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{comparison between '&f' and 'nullptr' has unspecified value}} \ - // expected-warning {{ordered comparison of function pointers}} \ - // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{comparison between '&f' and 'nullptr' has unspecified value}} + constexpr bool u14 = pf < (void(*)())nullptr; // both-warning {{ordered comparison of function pointers}} \ + // both-error {{must be initialized by a constant expression}} \ + // both-note {{comparison between '&f' and 'nullptr' has unspecified value}} @@ -249,31 +230,22 @@ static_assert(doit() == 10, ""); namespace InvalidCall { struct S { - constexpr int a() const { // expected-error {{never produces a constant expression}} \ - // ref-error {{never produces a constant expression}} - return 1 / 0; // expected-note 2{{division by zero}} \ - // expected-warning {{is undefined}} \ - // ref-note 2{{division by zero}} \ - // ref-warning {{is undefined}} + constexpr int a() const { // both-error {{never produces a constant expression}} + return 1 / 0; // both-note 2{{division by zero}} \ + // both-warning {{is undefined}} } }; constexpr S s; - static_assert(s.a() == 1, ""); // expected-error {{not an integral constant expression}} \ - // expected-note {{in call to}} \ - // ref-error {{not an integral constant expression}} \ - // ref-note {{in call to}} + static_assert(s.a() == 1, ""); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} /// This used to cause an assertion failure in the new constant interpreter. - constexpr void func(); // expected-note {{declared here}} \ - // ref-note {{declared here}} + constexpr void func(); // both-note {{declared here}} struct SS { - constexpr SS() { func(); } // expected-note {{undefined function }} \ - // ref-note {{undefined function}} + constexpr SS() { func(); } // both-note {{undefined function }} }; - constexpr SS ss; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{in call to 'SS()'}} \ - // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{in call to 'SS()'}} + constexpr SS ss; // both-error {{must be initialized by a constant expression}} \ + // both-note {{in call to 'SS()'}} /// This should not emit a diagnostic. @@ -299,8 +271,7 @@ namespace CallWithArgs { namespace ReturnLocalPtr { constexpr int *p() { int a = 12; - return &a; // ref-warning {{address of stack memory}} \ - // expected-warning {{address of stack memory}} + return &a; // both-warning {{address of stack memory}} } /// GCC rejects the expression below, just like the new interpreter. The current interpreter @@ -313,13 +284,11 @@ namespace ReturnLocalPtr { /// new one does not. constexpr const int &p2() { int a = 12; // ref-note {{declared here}} - return a; // ref-warning {{reference to stack memory associated with local variable}} \ - // expected-warning {{reference to stack memory associated with local variable}} + return a; // both-warning {{reference to stack memory associated with local variable}} } - static_assert(p2() == 12, ""); // ref-error {{not an integral constant expression}} \ - // ref-note {{read of variable whose lifetime has ended}} \ - // expected-error {{not an integral constant expression}} + static_assert(p2() == 12, ""); // both-error {{not an integral constant expression}} \ + // ref-note {{read of variable whose lifetime has ended}} } namespace VoidReturn { @@ -332,22 +301,16 @@ namespace VoidReturn { } namespace InvalidReclRefs { - void param(bool b) { // ref-note {{declared here}} \ - // expected-note {{declared here}} - static_assert(b, ""); // ref-error {{not an integral constant expression}} \ - // ref-note {{function parameter 'b' with unknown value}} \ - // expected-error {{not an integral constant expression}} \ - // expected-note {{function parameter 'b' with unknown value}} + void param(bool b) { // both-note {{declared here}} + static_assert(b, ""); // both-error {{not an integral constant expression}} \ + // both-note {{function parameter 'b' with unknown value}} static_assert(true ? true : b, ""); } #if __cplusplus >= 202002L - consteval void param2(bool b) { // ref-note {{declared here}} \ - // expected-note {{declared here}} - static_assert(b, ""); // ref-error {{not an integral constant expression}} \ - // ref-note {{function parameter 'b' with unknown value}} \ - // expected-error {{not an integral constant expression}} \ - // expected-note {{function parameter 'b' with unknown value}} + consteval void param2(bool b) { // both-note {{declared here}} + static_assert(b, ""); // both-error {{not an integral constant expression}} \ + // both-note {{function parameter 'b' with unknown value}} } #endif } @@ -482,13 +445,10 @@ namespace AddressOf { static_assert(&pt->n == &t.n, ""); struct U { int n : 5; } u; - int *pbf = __builtin_addressof(u.n); // expected-error {{address of bit-field requested}} \ - // ref-error {{address of bit-field requested}} + int *pbf = __builtin_addressof(u.n); // both-error {{address of bit-field requested}} - S *ptmp = __builtin_addressof(S{}); // expected-error {{taking the address of a temporary}} \ - // expected-warning {{temporary whose address is used as value of local variable 'ptmp' will be destroyed at the end of the full-expression}} \ - // ref-error {{taking the address of a temporary}} \ - // ref-warning {{temporary whose address is used as value of local variable 'ptmp' will be destroyed at the end of the full-expression}} + S *ptmp = __builtin_addressof(S{}); // both-error {{taking the address of a temporary}} \ + // both-warning {{temporary whose address is used as value of local variable 'ptmp' will be destroyed at the end of the full-expression}} constexpr int foo() {return 1;} static_assert(__builtin_addressof(foo) == foo, ""); @@ -509,8 +469,7 @@ constexpr typename std::remove_reference::type&& move(T &&t) noexcept { /// The std::move declaration above gets translated to a builtin function. namespace Move { #if __cplusplus >= 202002L - consteval int f_eval() { // expected-note 12{{declared here}} \ - // ref-note 12{{declared here}} + consteval int f_eval() { // both-note 12{{declared here}} return 0; } @@ -530,56 +489,35 @@ namespace Move { // there is no the copy constructor call when its argument is a prvalue because of garanteed copy elision. // so we need to test with both prvalue and xvalues. { Copy c(C); } - { Copy c((Copy(&f_eval))); } // expected-error {{cannot take address of consteval}} \ - // ref-error {{cannot take address of consteval}} + { Copy c((Copy(&f_eval))); } // both-error {{cannot take address of consteval}} { Copy c(std::move(C)); } - { Copy c(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} - { Copy c(to_lvalue_ref((Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} + { Copy c(std::move(Copy(&f_eval))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} + { Copy c(to_lvalue_ref((Copy(&f_eval)))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} { Copy c(to_lvalue_ref(std::move(C))); } - { Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} + { Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} { Copy c = Copy(C); } - { Copy c = Copy(Copy(&f_eval)); } // expected-error {{cannot take address of consteval}} \ - // ref-error {{cannot take address of consteval}} + { Copy c = Copy(Copy(&f_eval)); } // both-error {{cannot take address of consteval}} { Copy c = Copy(std::move(C)); } - { Copy c = Copy(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} - { Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} + { Copy c = Copy(std::move(Copy(&f_eval))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} + { Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} { Copy c = Copy(to_lvalue_ref(std::move(C))); } - { Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} + { Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} { Copy c; c = Copy(C); } - { Copy c; c = Copy(Copy(&f_eval)); } // expected-error {{cannot take address of consteval}} \ - // ref-error {{cannot take address of consteval}} + { Copy c; c = Copy(Copy(&f_eval)); } // both-error {{cannot take address of consteval}} { Copy c; c = Copy(std::move(C)); } - { Copy c; c = Copy(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} - { Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} + { Copy c; c = Copy(std::move(Copy(&f_eval))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} + { Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} { Copy c; c = Copy(to_lvalue_ref(std::move(C))); } - { Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \ - // expected-note {{to a consteval}} \ - // ref-error {{is not a constant expression}} \ - // ref-note {{to a consteval}} + { Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // both-error {{is not a constant expression}} \ + // both-note {{to a consteval}} } #endif constexpr int A = std::move(5); diff --git a/clang/test/Sema/warn-char-subscripts.c b/clang/test/Sema/warn-char-subscripts.c index 0a012f68feae0..c2f7a3731d72c 100644 --- a/clang/test/Sema/warn-char-subscripts.c +++ b/clang/test/Sema/warn-char-subscripts.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -Wchar-subscripts -fsyntax-only -verify %s +// RUN: %clang_cc1 -Wchar-subscripts -fsyntax-only -verify %s -fexperimental-new-constant-interpreter void t1(void) { int array[1] = { 0 };