| //===----- 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<Pointer>(&Value)) |
| return P->toAPValue(); |
| else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) |
| return FP->toAPValue(); |
| else |
| llvm_unreachable("Unhandled LValue type"); |
| break; |
| case RValue: |
| return std::get<APValue>(Value); |
| case Valid: |
| return APValue(); |
| default: |
| llvm_unreachable("Unhandled result kind?"); |
| } |
| } |
| |
| std::optional<APValue> 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<Pointer>(&Value)) |
| return P->toRValue(*Ctx); |
| else if (const auto *FP = std::get_if<FunctionPointer>(&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->getZExtSize(); |
| 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<ConstantArrayType>(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 (F.Decl->isUnnamedBitField()) { |
| // Nothing do do here. |
| } else if (FieldType->isArrayType()) { |
| const auto *CAT = |
| cast<ConstantArrayType>(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 Pointer &Ptr) const { |
| assert(Source); |
| assert(empty()); |
| |
| // Our Source must be a VarDecl. |
| const Decl *SourceDecl = Source.dyn_cast<const Decl *>(); |
| assert(SourceDecl); |
| const auto *VD = cast<VarDecl>(SourceDecl); |
| assert(VD->getType()->isRecordType() || VD->getType()->isArrayType()); |
| SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc(); |
| |
| assert(!Ptr.isZero()); |
| |
| if (const Record *R = Ptr.getRecord()) |
| return CheckFieldsInitialized(S, InitLoc, Ptr, R); |
| const auto *CAT = |
| cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe()); |
| return CheckArrayInitialized(S, InitLoc, Ptr, CAT); |
| } |
| |
| 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<APValue>(Value).dump(OS, ASTCtx); |
| break; |
| case LValue: { |
| assert(Source); |
| QualType SourceType; |
| if (const auto *D = Source.dyn_cast<const Decl *>()) { |
| if (const auto *VD = dyn_cast<ValueDecl>(D)) |
| SourceType = VD->getType(); |
| } else if (const auto *E = Source.dyn_cast<const Expr *>()) { |
| SourceType = E->getType(); |
| } |
| |
| OS << "LValue: "; |
| if (const auto *P = std::get_if<Pointer>(&Value)) |
| P->toAPValue().printPretty(OS, ASTCtx, SourceType); |
| else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope |
| FP->toAPValue().printPretty(OS, ASTCtx, SourceType); |
| OS << "\n"; |
| break; |
| } |
| case Invalid: |
| OS << "Invalid\n"; |
| break; |
| case Valid: |
| OS << "Valid\n"; |
| break; |
| } |
| } |
| |
| } // namespace interp |
| } // namespace clang |