https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/67131
None >From 0f52eec7bba4ecbd5df75d8a663f1c374771e283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Fri, 22 Sep 2023 15:09:14 +0200 Subject: [PATCH] [clang][Interp] Diagnose uninitialized bases --- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 4 +- clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 2 +- clang/lib/AST/Interp/Interp.cpp | 6 ++ clang/lib/AST/Interp/Interp.h | 6 ++ clang/lib/AST/Interp/Opcodes.td | 4 + .../constexpr-subobj-initialization.cpp | 73 +++++++++++++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 clang/test/AST/Interp/constexpr-subobj-initialization.cpp diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index e813d4fa651ceaf..3e9af148b7a5aab 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -535,7 +535,7 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, if (!this->visitInitializer(Init)) return false; - if (!this->emitPopPtr(E)) + if (!this->emitInitPtrPop(E)) return false; // Base initializers don't increase InitIndex, since they don't count // into the Record's fields. @@ -1671,7 +1671,7 @@ bool ByteCodeExprGen<Emitter>::visitZeroRecordInitializer(const Record *R, return false; if (!this->visitZeroRecordInitializer(B.R, E)) return false; - if (!this->emitPopPtr(E)) + if (!this->emitInitPtrPop(E)) return false; } diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index 22a6908daf8b3c2..302e04e84fe4af7 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -194,7 +194,7 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { return false; if (!this->visitInitializer(InitExpr)) return false; - if (!this->emitPopPtr(InitExpr)) + if (!this->emitInitPtrPop(InitExpr)) return false; } } diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index e1951574edb6288..8e851595963184c 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -467,6 +467,12 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, // Check Fields in all bases for (const Record::Base &B : R->bases()) { Pointer P = BasePtr.atField(B.Offset); + if (!P.isInitialized()) { + S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(), + diag::note_constexpr_uninitialized_base) + << B.Desc->getType(); + return false; + } Result &= CheckFieldsInitialized(S, OpPC, P, B.R); } diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 8453856e526a6b2..14ce1f4c99f3ef1 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1212,6 +1212,12 @@ inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) { return true; } +inline bool InitPtrPop(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.pop<Pointer>(); + Ptr.initialize(); + return true; +} + inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl, const Pointer &Ptr) { Pointer Base = Ptr; diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index eeb71db125fef73..73e88939d3734d8 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -301,6 +301,10 @@ def GetPtrBasePop : Opcode { let Args = [ArgUint32]; } +def InitPtrPop : Opcode { + let Args = []; +} + def GetPtrDerivedPop : Opcode { let Args = [ArgUint32]; } diff --git a/clang/test/AST/Interp/constexpr-subobj-initialization.cpp b/clang/test/AST/Interp/constexpr-subobj-initialization.cpp new file mode 100644 index 000000000000000..4976b165468bd61 --- /dev/null +++ b/clang/test/AST/Interp/constexpr-subobj-initialization.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter + +/// This is like the version in test/SemaCXX/, but some of the +/// output types and their location has been adapted. +/// Differences: +/// 1) The type of the uninitialized base class is printed WITH the namespace, +/// i.e. 'baseclass_uninit::DelBase' instead of just 'DelBase'. +/// 2) The location is not the base specifier declaration, but the call site +/// of the constructor. + + +namespace baseclass_uninit { +struct DelBase { + constexpr DelBase() = delete; // expected-note {{'DelBase' has been explicitly marked deleted here}} +}; + +struct Foo : DelBase { + constexpr Foo() {}; // expected-error {{call to deleted constructor of 'DelBase'}} +}; +constexpr Foo f; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'baseclass_uninit::DelBase' is not called}} + +struct Bar : Foo { + constexpr Bar() {}; +}; +constexpr Bar bar; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'baseclass_uninit::DelBase' is not called}} + +struct Base {}; +struct A : Base { + constexpr A() : value() {} // expected-error {{member initializer 'value' does not name a non-static data member or base class}} +}; + +constexpr A a; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'baseclass_uninit::Base' is not called}} + + +struct B : Base { + constexpr B() : {} // expected-error {{expected class member or base class name}} +}; + +constexpr B b; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{constructor of base class 'baseclass_uninit::Base' is not called}} +} // namespace baseclass_uninit + + +struct Foo { + constexpr Foo(); // expected-note 2{{declared here}} +}; + +constexpr Foo ff; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{undefined constructor 'Foo' cannot be used in a constant expression}} + +struct Bar : protected Foo { + int i; + constexpr Bar() : i(12) {} // expected-note {{undefined constructor 'Foo' cannot be used in a constant expression}} +}; + +constexpr Bar bb; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'Bar()'}} + +template <typename Ty> +struct Baz { + constexpr Baz(); // expected-note {{declared here}} +}; + +struct Quux : Baz<Foo>, private Bar { + int i; + constexpr Quux() : i(12) {} // expected-note {{undefined constructor 'Baz' cannot be used in a constant expression}} +}; + +constexpr Quux qx; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'Quux()'}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits