From ee2e414d66a4b3b4e1a3bade11168a108f349d8a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timm=20B=C3=A4der?= Date: Wed, 14 Sep 2022 16:53:55 +0200 Subject: [PATCH] [clang][Interp] Handle sizeof() Implement visiting UnaryExprOrTypeTraitExprs to handle sizeof() expressions. Differential Revision: https://reviews.llvm.org/D133934 --- clang/lib/AST/Interp/Boolean.h | 4 ++ clang/lib/AST/Interp/ByteCodeExprGen.cpp | 24 ++++++++++- clang/lib/AST/Interp/ByteCodeExprGen.h | 1 + clang/lib/AST/Interp/Opcodes.td | 4 +- clang/test/AST/Interp/literals.cpp | 72 ++++++++++++++++++++++++++++++-- 5 files changed, 99 insertions(+), 6 deletions(-) diff --git a/clang/lib/AST/Interp/Boolean.h b/clang/lib/AST/Interp/Boolean.h index e739ce2..f1a0b90 100644 --- a/clang/lib/AST/Interp/Boolean.h +++ b/clang/lib/AST/Interp/Boolean.h @@ -47,6 +47,10 @@ class Boolean { Boolean operator~() const { return Boolean(true); } explicit operator unsigned() const { return V; } + explicit operator int8_t() const { return V; } + explicit operator uint8_t() const { return V; } + explicit operator int16_t() const { return V; } + explicit operator uint16_t() const { return V; } explicit operator int64_t() const { return V; } explicit operator uint64_t() const { return V; } explicit operator int() const { return V; } diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index f86a7b1..5974678 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -281,7 +281,29 @@ bool ByteCodeExprGen::VisitConstantExpr(const ConstantExpr *E) { } template -bool ByteCodeExprGen::discard(const Expr *E) { +bool ByteCodeExprGen::VisitUnaryExprOrTypeTraitExpr( + const UnaryExprOrTypeTraitExpr *E) { + + if (E->getKind() == UETT_SizeOf) { + QualType ArgType = E->getTypeOfArgument(); + + CharUnits Size; + if (ArgType->isVoidType() || ArgType->isFunctionType()) + Size = CharUnits::One(); + else { + if (ArgType->isDependentType() || !ArgType->isConstantSizeType()) + return false; + + Size = Ctx.getASTContext().getTypeSizeInChars(ArgType); + } + + return this->emitConst(E, Size.getQuantity()); + } + + return false; +} + +template bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); } diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h index 6a74bc3..d73a8df 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -80,6 +80,7 @@ public: bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E); bool VisitInitListExpr(const InitListExpr *E); bool VisitConstantExpr(const ConstantExpr *E); + bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); protected: bool visitExpr(const Expr *E) override; diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 49cc0e0..c743807 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -427,11 +427,11 @@ def Neg: Opcode { // TODO: Expand this to handle casts between more types. def FromCastTypeClass : TypeClass { - let Types = [Uint32, Sint32, Bool]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; } def ToCastTypeClass : TypeClass { - let Types = [Uint32, Sint32, Bool]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; } def Cast: Opcode { diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index 5c1df00..5de727f 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++11 -verify %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify %s // RUN: %clang_cc1 -std=c++11 -verify=ref %s +// RUN: %clang_cc1 -std=c++20 -verify=ref %s static_assert(true, ""); static_assert(false, ""); // expected-error{{failed}} ref-error{{failed}} @@ -72,10 +74,74 @@ static_assert(*p == 10, ""); constexpr const int* getIntPointer() { return &m; } -//static_assert(getIntPointer() == &m, ""); TODO -//static_assert(*getIntPointer() == 10, ""); TODO +static_assert(getIntPointer() == &m, ""); +static_assert(*getIntPointer() == 10, ""); constexpr int gimme(int k) { return k; } -// static_assert(gimme(5) == 5, ""); TODO +static_assert(gimme(5) == 5, ""); + +namespace SizeOf { + constexpr int soint = sizeof(int); + constexpr int souint = sizeof(unsigned int); + static_assert(soint == souint, ""); + + static_assert(sizeof(&soint) == sizeof(void*), ""); + static_assert(sizeof(&soint) == sizeof(nullptr), ""); + + static_assert(sizeof(long) == sizeof(unsigned long), ""); + static_assert(sizeof(char) == sizeof(unsigned char), ""); + + constexpr int N = 4; + constexpr int arr[N] = {1,2,3,4}; + static_assert(sizeof(arr) == N * sizeof(int), ""); + static_assert(sizeof(arr) == N * sizeof(arr[0]), ""); + + constexpr bool arrB[N] = {true, true, true, true}; + static_assert(sizeof(arrB) == N * sizeof(bool), ""); + + static_assert(sizeof(bool) == 1, ""); + static_assert(sizeof(char) == 1, ""); + + constexpr int F = sizeof(void); // expected-error{{incomplete type 'void'}} \ + // ref-error{{incomplete type 'void'}} + + constexpr int F2 = sizeof(gimme); // expected-error{{to a function type}} \ + // ref-error{{to a function type}} + + + + /// FIXME: The following code should be accepted. + struct S { + void func(); + }; + constexpr void (S::*Func)() = &S::func; // expected-error {{must be initialized by a constant expression}} \ + // expected-error {{interpreter failed to evaluate an expression}} + static_assert(sizeof(Func) == sizeof(&S::func), ""); + + + void func() { + int n = 12; + constexpr int oofda = sizeof(int[n++]); // expected-error {{must be initialized by a constant expression}} \ + // ref-error {{must be initialized by a constant expression}} + } + + +#if __cplusplus >= 202002L + /// FIXME: The following code should be accepted. + consteval int foo(int n) { // ref-error {{consteval function never produces a constant expression}} + return sizeof(int[n]); // ref-note 3{{not valid in a constant expression}} \ + // expected-note {{not valid in a constant expression}} + } + constinit int var = foo(5); // ref-error {{not a constant expression}} \ + // ref-note 2{{in call to}} \ + // ref-error {{does not have a constant initializer}} \ + // ref-note {{required by 'constinit' specifier}} \ + // expected-error {{is not a constant expression}} \ + // expected-note {{in call to}} \ + // expected-error {{does not have a constant initializer}} \ + // expected-note {{required by 'constinit' specifier}} \ + +#endif +}; -- 2.7.4