Skip to content

Commit

Permalink
[clang][Interp] Diagnose uninitialized bases (#67131)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaederr committed Oct 3, 2023
1 parent fb0a7c8 commit f58d54a
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 3 deletions.
4 changes: 2 additions & 2 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,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.
Expand Down Expand Up @@ -1718,7 +1718,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;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/ByteCodeStmtGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,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;
}
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,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;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ def GetPtrBasePop : Opcode {
let Args = [ArgUint32];
}

def InitPtrPop : Opcode {
let Args = [];
}

def GetPtrDerivedPop : Opcode {
let Args = [ArgUint32];
}
Expand Down
73 changes: 73 additions & 0 deletions clang/test/AST/Interp/constexpr-subobj-initialization.cpp
Original file line number Diff line number Diff line change
@@ -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()'}}

0 comments on commit f58d54a

Please sign in to comment.