//===----- EvaluationResult.cpp - Result class for the VM ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "EvaluationResult.h" #include "Context.h" #include "InterpState.h" #include "Record.h" #include "clang/AST/ExprCXX.h" namespace clang { namespace interp { APValue EvaluationResult::toAPValue() const { assert(!empty()); switch (Kind) { case LValue: // Either a pointer or a function pointer. if (const auto *P = std::get_if(&Value)) return P->toAPValue(); else if (const auto *FP = std::get_if(&Value)) return FP->toAPValue(); else llvm_unreachable("Unhandled LValue type"); break; case RValue: return std::get(Value); case Valid: return APValue(); default: llvm_unreachable("Unhandled result kind?"); } } std::optional EvaluationResult::toRValue() const { if (Kind == RValue) return toAPValue(); assert(Kind == LValue); // We have a pointer and want an RValue. if (const auto *P = std::get_if(&Value)) return P->toRValue(*Ctx); else if (const auto *FP = std::get_if(&Value)) // Nope return FP->toAPValue(); llvm_unreachable("Unhandled lvalue kind"); } static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, const FieldDecl *SubObjDecl) { assert(SubObjDecl && "Subobject declaration does not exist"); S.FFDiag(Loc, diag::note_constexpr_uninitialized) << /*(name)*/ 1 << SubObjDecl; S.Note(SubObjDecl->getLocation(), diag::note_constexpr_subobject_declared_here); } static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const Record *R); static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const ConstantArrayType *CAT) { bool Result = true; size_t NumElems = CAT->getSize().getZExtValue(); QualType ElemType = CAT->getElementType(); if (ElemType->isRecordType()) { const Record *R = BasePtr.getElemRecord(); for (size_t I = 0; I != NumElems; ++I) { Pointer ElemPtr = BasePtr.atIndex(I).narrow(); Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R); } } else if (const auto *ElemCAT = dyn_cast(ElemType)) { for (size_t I = 0; I != NumElems; ++I) { Pointer ElemPtr = BasePtr.atIndex(I).narrow(); Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT); } } else { for (size_t I = 0; I != NumElems; ++I) { if (!BasePtr.atIndex(I).isInitialized()) { DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField()); Result = false; } } } return Result; } static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const Record *R) { assert(R); bool Result = true; // Check all fields of this record are initialized. for (const Record::Field &F : R->fields()) { Pointer FieldPtr = BasePtr.atField(F.Offset); QualType FieldType = F.Decl->getType(); if (FieldType->isRecordType()) { Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord()); } else if (FieldType->isIncompleteArrayType()) { // Nothing to do here. } else if (FieldType->isArrayType()) { const auto *CAT = cast(FieldType->getAsArrayTypeUnsafe()); Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT); } else if (!FieldPtr.isInitialized()) { DiagnoseUninitializedSubobject(S, Loc, F.Decl); Result = false; } } // 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, Loc, P, B.R); } // TODO: Virtual bases return Result; } bool EvaluationResult::checkFullyInitialized(InterpState &S) const { assert(Source); assert(isLValue()); // Our Source must be a VarDecl. const Decl *SourceDecl = Source.dyn_cast(); assert(SourceDecl); const auto *VD = cast(SourceDecl); 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()) return CheckFieldsInitialized(S, InitLoc, Ptr, R); const auto *CAT = cast(Ptr.getType()->getAsArrayTypeUnsafe()); return CheckArrayInitialized(S, InitLoc, Ptr, CAT); return true; } void EvaluationResult::dump() const { assert(Ctx); auto &OS = llvm::errs(); const ASTContext &ASTCtx = Ctx->getASTContext(); switch (Kind) { case Empty: OS << "Empty\n"; break; case RValue: OS << "RValue: "; std::get(Value).dump(OS, ASTCtx); break; case LValue: { assert(Source); QualType SourceType; if (const auto *D = Source.dyn_cast()) { if (const auto *VD = dyn_cast(D)) SourceType = VD->getType(); } else if (const auto *E = Source.dyn_cast()) { SourceType = E->getType(); } OS << "LValue: "; if (const auto *P = std::get_if(&Value)) P->toAPValue().printPretty(OS, ASTCtx, SourceType); else if (const auto *FP = std::get_if(&Value)) // Nope FP->toAPValue().printPretty(OS, ASTCtx, SourceType); OS << "\n"; break; } default: llvm_unreachable("Can't print that."); } } } // namespace interp } // namespace clang