[clang] [clang][Interp] Handle unknown-size arrays better (PR #68868)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 12 02:53:15 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

We unfortunately actually need to do some checks for array-to-pointer decays it seems.

---
Full diff: https://github.com/llvm/llvm-project/pull/68868.diff


6 Files Affected:

- (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+13-1) 
- (modified) clang/lib/AST/Interp/EvalEmitter.cpp (+6) 
- (modified) clang/lib/AST/Interp/Interp.cpp (+2) 
- (modified) clang/lib/AST/Interp/Interp.h (+20) 
- (modified) clang/lib/AST/Interp/Opcodes.td (+2) 
- (modified) clang/test/AST/Interp/arrays.cpp (+45) 


``````````diff
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 2b745d6a1509868..da4e4a58207cfce 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -162,7 +162,16 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     return this->emitCastPointerIntegral(T, CE);
   }
 
-  case CK_ArrayToPointerDecay:
+  case CK_ArrayToPointerDecay: {
+    if (!this->visit(SubExpr))
+      return false;
+    if (!this->emitArrayDecay(CE))
+      return false;
+    if (DiscardResult)
+      return this->emitPopPtr(CE);
+    return true;
+  }
+
   case CK_AtomicToNonAtomic:
   case CK_ConstructorConversion:
   case CK_FunctionToPointerDecay:
@@ -499,6 +508,9 @@ bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueIni
   if (QT->isRecordType())
     return false;
 
+  if (QT->isIncompleteArrayType())
+    return true;
+
   if (QT->isArrayType()) {
     const ArrayType *AT = QT->getAsArrayTypeUnsafe();
     assert(AT);
diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index f46ef1067cf52a0..466327e2c7c7b86 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -177,6 +177,12 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
       }
       return Ok;
     }
+
+    if (Ty->isIncompleteArrayType()) {
+      R = APValue(APValue::UninitArray(), 0, 0);
+      return true;
+    }
+
     if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
       const size_t NumElems = Ptr.getNumElems();
       QualType ElemTy = AT->getElementType();
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index a4d6844ebe61722..41edbfe0da3f381 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -454,6 +454,8 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
 
     if (FieldType->isRecordType()) {
       Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord());
+    } else if (FieldType->isIncompleteArrayType()) {
+      // Nothing to do here.
     } else if (FieldType->isArrayType()) {
       const auto *CAT =
           cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 1ad3b8bfc7711d3..bf285e2701814ca 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1797,17 +1797,37 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
   const T &Offset = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.peek<Pointer>();
 
+  if (!CheckArray(S, OpPC, Ptr))
+    return false;
+
   if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
     return false;
 
   return NarrowPtr(S, OpPC);
 }
 
+/// Just takes a pointer and checks if its' an incomplete
+/// array type.
+inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
+  const Pointer &Ptr = S.Stk.peek<Pointer>();
+
+  if (!Ptr.isUnknownSizeArray())
+    return true;
+
+  const SourceInfo &E = S.Current->getSource(OpPC);
+  S.FFDiag(E, diag::note_constexpr_unsupported_unsized_array);
+
+  return false;
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
   const T &Offset = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.pop<Pointer>();
 
+  if (!CheckArray(S, OpPC, Ptr))
+    return false;
+
   if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
     return false;
 
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 9d390fed152417f..29c83e7ba9efc64 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -674,3 +674,5 @@ def InvalidCast : Opcode {
 def InvalidDeclRef : Opcode {
   let Args = [ArgDeclRef];
 }
+
+def ArrayDecay : Opcode;
diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp
index 281835f828bbd7c..919e3407d3afa05 100644
--- a/clang/test/AST/Interp/arrays.cpp
+++ b/clang/test/AST/Interp/arrays.cpp
@@ -390,3 +390,48 @@ namespace StringZeroFill {
   static_assert(b[4] == '\0', "");
   static_assert(b[5] == '\0', "");
 }
+
+namespace Incomplete {
+  struct Foo {
+    char c;
+    int a[];
+  };
+
+  constexpr Foo F{};
+  constexpr const int *A = F.a; // ref-error {{must be initialized by a constant expression}} \
+                                // ref-note {{array-to-pointer decay of array member without known bound}} \
+                                // expected-error {{must be initialized by a constant expression}} \
+                                // expected-note {{array-to-pointer decay of array member without known bound}}
+
+  constexpr const int *B = F.a + 1; // ref-error {{must be initialized by a constant expression}} \
+                                    // ref-note {{array-to-pointer decay of array member without known bound}} \
+                                    // expected-error {{must be initialized by a constant expression}} \
+                                    // expected-note {{array-to-pointer decay of array member without known bound}}
+
+  constexpr int C = *F.a; // ref-error {{must be initialized by a constant expression}} \
+                          // ref-note {{array-to-pointer decay of array member without known bound}} \
+                          // expected-error {{must be initialized by a constant expression}} \
+                          // expected-note {{array-to-pointer decay of array member without known bound}}
+
+
+
+  /// These are from test/SemaCXX/constant-expression-cxx11.cpp
+  /// and are the only tests using the 'indexing of array without known bound' diagnostic.
+  /// We currently diagnose them differently.
+  extern int arr[]; // expected-note 3{{declared here}}
+  constexpr int *c = &arr[1]; // ref-error  {{must be initialized by a constant expression}} \
+                              // ref-note {{indexing of array without known bound}} \
+                              // expected-error {{must be initialized by a constant expression}} \
+                              // expected-note {{read of non-constexpr variable 'arr'}}
+  constexpr int *d = &arr[1]; // ref-error  {{must be initialized by a constant expression}} \
+                              // ref-note {{indexing of array without known bound}} \
+                              // expected-error {{must be initialized by a constant expression}} \
+                              // expected-note {{read of non-constexpr variable 'arr'}}
+  constexpr int *e = arr + 1; // ref-error  {{must be initialized by a constant expression}} \
+                              // ref-note {{indexing of array without known bound}} \
+                              // expected-error {{must be initialized by a constant expression}} \
+                              // expected-note {{read of non-constexpr variable 'arr'}}
+
+
+
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/68868


More information about the cfe-commits mailing list