diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index a255a1fdf6f16..16df9b2737407 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1242,9 +1242,11 @@ inline bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { } } - unsigned VL = LHS.computeOffsetForComparison(S.getASTContext()); - unsigned VR = RHS.computeOffsetForComparison(S.getASTContext()); - S.Stk.push(BoolT::from(Fn(Compare(VL, VR)))); + std::optional VL = LHS.computeOffsetForComparison(S.getASTContext()); + std::optional VR = RHS.computeOffsetForComparison(S.getASTContext()); + if (!VL || !VR) + return Invalid(S, OpPC); + S.Stk.push(BoolT::from(Fn(Compare(*VL, *VR)))); return true; } @@ -1315,10 +1317,12 @@ inline bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { } if (Pointer::hasSameBase(LHS, RHS)) { - size_t A = LHS.computeOffsetForComparison(S.getASTContext()); - size_t B = RHS.computeOffsetForComparison(S.getASTContext()); + std::optional A = LHS.computeOffsetForComparison(S.getASTContext()); + std::optional B = RHS.computeOffsetForComparison(S.getASTContext()); + if (!A || !B) + return Invalid(S, OpPC); - S.Stk.push(BoolT::from(Fn(Compare(A, B)))); + S.Stk.push(BoolT::from(Fn(Compare(*A, *B)))); return true; } diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 51ef1401c3603..93357653c6f5d 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -359,9 +359,13 @@ void Pointer::print(llvm::raw_ostream &OS) const { /// with the same base. To get accurate results, we basically _have to_ compute /// the lvalue offset using the ASTRecordLayout. /// -/// FIXME: We're still mixing values from the record layout with our internal -/// offsets, which will inevitably lead to cryptic errors. -size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { +/// This function will fail if we're trying to get the type size of a forward +/// declaration. +/// +// FIXME: We're still mixing values from the record layout with our internal +// offsets, which will inevitably lead to cryptic errors. +std::optional +Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { switch (StorageKind) { case Storage::Int: return Int.Value + Offset; @@ -374,6 +378,15 @@ size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { return reinterpret_cast(asTypeidPointer().TypePtr) + Offset; } + auto getTypeSize = [&](QualType T) -> std::optional { + if (const RecordType *RT = T->getAs()) { + // We cannot get the type size of a forward declaration. + if (!RT->getDecl()->getDefinition()) + return std::nullopt; + } + return ASTCtx.getTypeSizeInChars(T).getQuantity(); + }; + size_t Result = 0; Pointer P = *this; while (true) { @@ -397,9 +410,12 @@ size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { } if (P.isRoot()) { - if (P.isOnePastEnd()) - Result += - ASTCtx.getTypeSizeInChars(P.getDeclDesc()->getType()).getQuantity(); + if (P.isOnePastEnd()) { + if (auto Size = getTypeSize(P.getDeclDesc()->getType())) + Result += *Size; + else + return std::nullopt; + } break; } @@ -413,9 +429,12 @@ size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { Layout.getFieldOffset(P.getField()->getFieldIndex())) .getQuantity(); - if (P.isOnePastEnd()) - Result += - ASTCtx.getTypeSizeInChars(P.getField()->getType()).getQuantity(); + if (P.isOnePastEnd()) { + if (auto Size = getTypeSize(P.getField()->getType())) + Result += *Size; + else + return std::nullopt; + } P = P.getBase(); if (P.isRoot()) diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 3eb9cfc4e53db..50c38f206edba 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -843,7 +843,8 @@ class Pointer { /// Compute an integer that can be used to compare this pointer to /// another one. This is usually NOT the same as the pointer offset /// regarding the AST record layout. - size_t computeOffsetForComparison(const ASTContext &ASTCtx) const; + std::optional + computeOffsetForComparison(const ASTContext &ASTCtx) const; private: friend class Block; diff --git a/clang/test/AST/ByteCode/const-eval.c b/clang/test/AST/ByteCode/const-eval.c index 1d30c87374c05..5d1889efd1ffc 100644 --- a/clang/test/AST/ByteCode/const-eval.c +++ b/clang/test/AST/ByteCode/const-eval.c @@ -189,3 +189,7 @@ _Static_assert(((union ToUnion_U)9).z == 1, ""); struct S s; // both-error {{tentative definition has type 'struct S' that is never completed}} \ // both-note {{forward declaration of 'struct S'}} int foo[2 * ((long)&s + 42i) == 2]; // both-error {{variable length array declaration not allowed at file scope}} + +struct FD fd; // both-error {{tentative definition has type 'struct FD' that is never completed}} \ + // both-note {{forward declaration of 'struct FD'}} +EVAL_EXPR(55, &fd < (struct FD *)((int *)&fd + 42)) // both-error {{not an integer constant expression}}