Skip to content

Commit

Permalink
[clang][Interp] Diagnose unknown parameter values
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D156509
  • Loading branch information
tbaederr committed Sep 15, 2023
1 parent 177f9ab commit 23c39f9
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 3 deletions.
3 changes: 2 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2492,7 +2492,8 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
return this->VisitDeclRefExpr(E);
}
}
return false;

return this->emitInvalidDeclRef(E, E);
}

template <class Emitter>
Expand Down
32 changes: 32 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,38 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
return true;
}

/// We aleady know the given DeclRefExpr is invalid for some reason,
/// now figure out why and print appropriate diagnostics.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
const ValueDecl *D = DR->getDecl();
const SourceInfo &E = S.Current->getSource(OpPC);

if (isa<ParmVarDecl>(D)) {
S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D;
S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (!VD->getType().isConstQualified()) {
S.FFDiag(E,
VD->getType()->isIntegralOrEnumerationType()
? diag::note_constexpr_ltor_non_const_int
: diag::note_constexpr_ltor_non_constexpr,
1)
<< VD;
S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
return false;
}

// const, but no initializer.
if (!VD->getAnyInitializer()) {
S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD;
S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
return false;
}
}

return false;
}

bool Interpret(InterpState &S, APValue &Result) {
// The current stack frame when we started Interpret().
// This is being used by the ops to determine wheter
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
APFloat::opStatus Status);

/// Checks why the given DeclRefExpr is invalid.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);

/// Interpreter entry point.
bool Interpret(InterpState &S, APValue &Result);

Expand Down Expand Up @@ -1854,6 +1857,12 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind) {
return false;
}

inline bool InvalidDeclRef(InterpState &S, CodePtr OpPC,
const DeclRefExpr *DR) {
assert(DR);
return CheckDeclRef(S, OpPC, DR);
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
llvm::SmallVector<int64_t> ArrayIndices;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; }
def ArgCastKind : ArgType { let Name = "CastKind"; }
def ArgCallExpr : ArgType { let Name = "const CallExpr *"; }
def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }

//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
Expand Down Expand Up @@ -632,3 +633,7 @@ def Invalid : Opcode {}
def InvalidCast : Opcode {
let Args = [ArgCastKind];
}

def InvalidDeclRef : Opcode {
let Args = [ArgDeclRef];
}
14 changes: 12 additions & 2 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
// RUN: %clang_cc1 -verify=ref -std=c11 %s
// RUN: %clang_cc1 -pedantic -verify=pedantic-ref -std=c11 %s

/// expected-no-diagnostics

_Static_assert(1, "");
_Static_assert(0 != 1, "");
_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
Expand All @@ -26,3 +24,15 @@ const int b = 3;
_Static_assert(b == 3, ""); // pedantic-ref-warning {{not an integer constant expression}} \
// pedantic-expected-warning {{not an integer constant expression}}

const int c; // ref-note {{declared here}} \
// pedantic-ref-note {{declared here}} \
// expected-note {{declared here}} \
// pedantic-expected-note {{declared here}}
_Static_assert(c == 0, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{initializer of 'c' is unknown}} \
// pedantic-ref-error {{not an integral constant expression}} \
// pedantic-ref-note {{initializer of 'c' is unknown}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{initializer of 'c' is unknown}} \
// pedantic-expected-error {{not an integral constant expression}} \
// pedantic-expected-note {{initializer of 'c' is unknown}}
25 changes: 25 additions & 0 deletions clang/test/AST/Interp/functions.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -verify=ref %s
// RUN: %clang_cc1 -std=c++14 -verify=ref %s
// RUN: %clang_cc1 -std=c++20 -verify=ref %s

constexpr void doNothing() {}
constexpr int gimme5() {
Expand Down Expand Up @@ -307,3 +311,24 @@ namespace VoidReturn {
}
static_assert((foo(),1) == 1, "");
}

namespace InvalidReclRefs {
void param(bool b) { // ref-note {{declared here}} \
// expected-note {{declared here}}
static_assert(b, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{function parameter 'b' with unknown value}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{function parameter 'b' with unknown value}}
static_assert(true ? true : b, "");
}

#if __cplusplus >= 202002L
consteval void param2(bool b) { // ref-note {{declared here}} \
// expected-note {{declared here}}
static_assert(b, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{function parameter 'b' with unknown value}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{function parameter 'b' with unknown value}}
}
#endif
}
28 changes: 28 additions & 0 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1088,3 +1088,31 @@ namespace PointerCasts {
int array[(intptr_t)(char*)0]; // ref-warning {{variable length array folded to constant array}} \
// expected-warning {{variable length array folded to constant array}}
}

namespace InvalidDeclRefs {
bool b00; // ref-note {{declared here}} \
// expected-note {{declared here}}
static_assert(b00, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{read of non-const variable}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{read of non-const variable}}

float b01; // ref-note {{declared here}} \
// expected-note {{declared here}}
static_assert(b01, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{read of non-constexpr variable}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{read of non-constexpr variable}}

extern const int b02; // ref-note {{declared here}} \
// expected-note {{declared here}}
static_assert(b02, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{initializer of 'b02' is unknown}} \
// expected-error {{not an integral constant expression}} \
// expected-note {{initializer of 'b02' is unknown}}

/// FIXME: This should also be diagnosed in the new interpreter.
int b03 = 3; // ref-note {{declared here}}
static_assert(b03, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{read of non-const variable}}
}

0 comments on commit 23c39f9

Please sign in to comment.