diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index 6068b1a5680c8..578dc44fe4f8d 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -88,7 +88,10 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, assert(Stk.empty()); ByteCodeExprGen C(*this, *P, Parent, Stk, Result); - auto Res = C.interpretDecl(VD); + bool CheckGlobalInitialized = + shouldBeGloballyIndexed(VD) && + (VD->getType()->isRecordType() || VD->getType()->isArrayType()); + auto Res = C.interpretDecl(VD, CheckGlobalInitialized); if (Res.isInvalid()) { Stk.clear(); return false; @@ -101,25 +104,7 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, Stk.clear(); #endif - // Ensure global variables are fully initialized. - if (shouldBeGloballyIndexed(VD) && - (VD->getType()->isRecordType() || VD->getType()->isArrayType() || - VD->getType()->isAnyComplexType())) { - assert(Res.isLValue()); - - if (!VD->getType()->isAnyComplexType() && - !Res.checkFullyInitialized(C.getState())) - return false; - - // lvalue-to-rvalue conversion. We do this manually here so we can - // examine the result above before converting and returning it. - std::optional RValueResult = Res.toRValue(); - if (!RValueResult) - return false; - Result = *RValueResult; - - } else - Result = Res.toAPValue(); + Result = Res.toAPValue(); return true; } diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp index f14023a23af9b..6715921ff1d97 100644 --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/clang/lib/AST/Interp/EvalEmitter.cpp @@ -44,7 +44,9 @@ EvaluationResult EvalEmitter::interpretExpr(const Expr *E, return std::move(this->EvalResult); } -EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD) { +EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD, + bool CheckFullyInitialized) { + this->CheckFullyInitialized = CheckFullyInitialized; EvalResult.setSource(VD); if (!this->visitDecl(VD) && EvalResult.empty()) @@ -131,7 +133,17 @@ template <> bool EvalEmitter::emitRet(const SourceInfo &Info) { return false; } } else { - EvalResult.setPointer(Ptr); + if (CheckFullyInitialized) { + if (!EvalResult.checkFullyInitialized(S, Ptr)) + return false; + + std::optional RValueResult = Ptr.toRValue(Ctx); + if (!RValueResult) + return false; + EvalResult.setValue(*RValueResult); + } else { + EvalResult.setValue(Ptr.toAPValue()); + } } return true; diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h index 8159e489f168e..032c8860ee677 100644 --- a/clang/lib/AST/Interp/EvalEmitter.h +++ b/clang/lib/AST/Interp/EvalEmitter.h @@ -36,7 +36,7 @@ class EvalEmitter : public SourceMapper { EvaluationResult interpretExpr(const Expr *E, bool ConvertResultToRValue = false); - EvaluationResult interpretDecl(const VarDecl *VD); + EvaluationResult interpretDecl(const VarDecl *VD, bool CheckFullyInitialized); InterpState &getState() { return S; } @@ -89,6 +89,9 @@ class EvalEmitter : public SourceMapper { EvaluationResult EvalResult; /// Whether the result should be converted to an RValue. bool ConvertResultToRValue = false; + /// Whether we should check if the result has been fully + /// initialized. + bool CheckFullyInitialized = false; /// Temporaries which require storage. llvm::DenseMap> Locals; diff --git a/clang/lib/AST/Interp/EvaluationResult.cpp b/clang/lib/AST/Interp/EvaluationResult.cpp index b44e8f84a1c42..693ae9ce4a94e 100644 --- a/clang/lib/AST/Interp/EvaluationResult.cpp +++ b/clang/lib/AST/Interp/EvaluationResult.cpp @@ -132,9 +132,10 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, return Result; } -bool EvaluationResult::checkFullyInitialized(InterpState &S) const { +bool EvaluationResult::checkFullyInitialized(InterpState &S, + const Pointer &Ptr) const { assert(Source); - assert(isLValue()); + assert(empty()); // Our Source must be a VarDecl. const Decl *SourceDecl = Source.dyn_cast(); @@ -143,7 +144,6 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S) const { assert(VD->getType()->isRecordType() || VD->getType()->isArrayType()); SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc(); - const Pointer &Ptr = *std::get_if(&Value); assert(!Ptr.isZero()); if (const Record *R = Ptr.getRecord()) diff --git a/clang/lib/AST/Interp/EvaluationResult.h b/clang/lib/AST/Interp/EvaluationResult.h index 52a6c011e39e1..28e1ae6ba3e7a 100644 --- a/clang/lib/AST/Interp/EvaluationResult.h +++ b/clang/lib/AST/Interp/EvaluationResult.h @@ -97,7 +97,7 @@ class EvaluationResult final { /// LValue and we can't read from it. std::optional toRValue() const; - bool checkFullyInitialized(InterpState &S) const; + bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const; /// Dump to stderr. void dump() const; diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c index 31cd2729f0bc7..fe540dc83918f 100644 --- a/clang/test/AST/Interp/c.c +++ b/clang/test/AST/Interp/c.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -verify=expected,all -std=c11 %s -// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -pedantic -verify=pedantic-expected,all -std=c11 %s -// RUN: %clang_cc1 -triple x86_64-linux -verify=ref,all -std=c11 %s -// RUN: %clang_cc1 -triple x86_64-linux -pedantic -verify=pedantic-ref,all -std=c11 %s +// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -verify=expected,all -std=c11 -Wcast-qual %s +// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -pedantic -verify=pedantic-expected,all -std=c11 -Wcast-qual %s +// RUN: %clang_cc1 -triple x86_64-linux -verify=ref,all -std=c11 -Wcast-qual %s +// RUN: %clang_cc1 -triple x86_64-linux -pedantic -verify=pedantic-ref,all -std=c11 -Wcast-qual %s typedef __INTPTR_TYPE__ intptr_t; typedef __PTRDIFF_TYPE__ ptrdiff_t; @@ -138,3 +138,15 @@ void t14(void) { // pedantic-ref-warning {{array index -1 is before the beginning of the array}} } + +void bar_0(void) { + struct C { + const int a; + int b; + }; + + const struct C S = {0, 0}; + + *(int *)(&S.a) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}} + *(int *)(&S.b) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}} +}