[clang] [clang][Interp] Do r-to-l conversion immediately when returning (PR #80662)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 14 22:27:00 PST 2024


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/80662 at github.com>


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/80662

>From b93916c9798ea09488e30b9b0aae9e54ef0b1956 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 15 Feb 2024 07:03:58 +0100
Subject: [PATCH 1/2] [clang][Interp][NFC] Convert test to verify=expected,both
 style

---
 clang/test/AST/Interp/functions.cpp | 198 ++++++++++------------------
 1 file changed, 68 insertions(+), 130 deletions(-)

diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp
index 34a832c794c75d..320691336bdd99 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<typename A, typename B>
 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<int, int>(), "");
 static_assert(sameSize<unsigned int, int>(), "");
-static_assert(sameSize<char, long>(), ""); // expected-note {{in instantiation of function template specialization}} \
-                                           // ref-note {{in instantiation of function template specialization}}
+static_assert(sameSize<char, long>(), ""); // 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<T>::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);

>From aa4ebdd6928cd4e6a8a39a4fd38699037e76fdea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 5 Feb 2024 10:29:45 +0100
Subject: [PATCH 2/2] [clang][Interp] Do r-to-l conversion immediately when
 returning

First, we need to register local constant variables in C, so we get
the same diagnostic behavior as the current interpeter.

Second, when returning an LValue (as a Pointer), which we eventually
convert to an RValue, we need to do the conversion immediately when
saving the Pointer in the EvaluationResult. Otherwise, we will
possibly deallocate the data before doing the conversion (which will
look at the Block*).
---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp |  3 +--
 clang/lib/AST/Interp/Context.cpp         | 16 ++++------------
 clang/lib/AST/Interp/EvalEmitter.cpp     | 20 ++++++++++++++++++--
 clang/lib/AST/Interp/EvalEmitter.h       |  5 ++++-
 clang/lib/AST/Interp/EvaluationResult.h  |  2 +-
 clang/test/AST/Interp/c.c                | 13 +++++++++++++
 clang/test/Sema/warn-char-subscripts.c   |  1 +
 7 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 988765972a36e6..31e7f02dd4305c 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -3243,8 +3243,7 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
   // This happens in C.
   if (!Ctx.getLangOpts().CPlusPlus) {
     if (const auto *VD = dyn_cast<VarDecl>(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 7396db22943663..6068b1a5680c83 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<EvalEmitter> 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<APValue> 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<APValue> RValueResult = Res.toRValue();
     if (!RValueResult)
       return false;
diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index c1e4ce3ebb0729..f14023a23af9b3 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 <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
   if (!isActive())
     return true;
-  EvalResult.setPointer(S.Stk.pop<Pointer>());
+
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  // Implicitly convert lvalue to rvalue, if requested.
+  if (ConvertResultToRValue) {
+    if (std::optional<APValue> V = Ptr.toRValue(Ctx)) {
+      EvalResult.setValue(*V);
+    } else {
+      return false;
+    }
+  } else {
+    EvalResult.setPointer(Ptr);
+  }
+
   return true;
 }
 template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
   if (!isActive())
     return true;
+  // Function pointers cannot be converted to rvalues.
+  assert(!ConvertResultToRValue);
   EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
   return true;
 }
diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h
index deb2ebc4e61fa0..8159e489f168e3 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<unsigned, std::unique_ptr<char[]>> Locals;
diff --git a/clang/lib/AST/Interp/EvaluationResult.h b/clang/lib/AST/Interp/EvaluationResult.h
index 2b9fc16f1a0abc..52a6c011e39e1b 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 85c195d33a96d7..31cd2729f0bc72 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/Sema/warn-char-subscripts.c b/clang/test/Sema/warn-char-subscripts.c
index 0a012f68feae07..c2f7a3731d72c8 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 };



More information about the cfe-commits mailing list