1//===----- EvaluationResult.cpp - Result class  for the VM ------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "EvaluationResult.h"
10#include "Context.h"
11#include "InterpState.h"
12#include "Record.h"
13#include "clang/AST/ExprCXX.h"
14
15namespace clang {
16namespace interp {
17
18APValue EvaluationResult::toAPValue() const {
19  assert(!empty());
20  switch (Kind) {
21  case LValue:
22    // Either a pointer or a function pointer.
23    if (const auto *P = std::get_if<Pointer>(&Value))
24      return P->toAPValue();
25    else if (const auto *FP = std::get_if<FunctionPointer>(&Value))
26      return FP->toAPValue();
27    else
28      llvm_unreachable("Unhandled LValue type");
29    break;
30  case RValue:
31    return std::get<APValue>(Value);
32  case Valid:
33    return APValue();
34  default:
35    llvm_unreachable("Unhandled result kind?");
36  }
37}
38
39std::optional<APValue> EvaluationResult::toRValue() const {
40  if (Kind == RValue)
41    return toAPValue();
42
43  assert(Kind == LValue);
44
45  // We have a pointer and want an RValue.
46  if (const auto *P = std::get_if<Pointer>(&Value))
47    return P->toRValue(*Ctx);
48  else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
49    return FP->toAPValue();
50  llvm_unreachable("Unhandled lvalue kind");
51}
52
53static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
54                                           const FieldDecl *SubObjDecl) {
55  assert(SubObjDecl && "Subobject declaration does not exist");
56  S.FFDiag(Loc, diag::note_constexpr_uninitialized)
57      << /*(name)*/ 1 << SubObjDecl;
58  S.Note(SubObjDecl->getLocation(),
59         diag::note_constexpr_subobject_declared_here);
60}
61
62static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
63                                   const Pointer &BasePtr, const Record *R);
64
65static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,
66                                  const Pointer &BasePtr,
67                                  const ConstantArrayType *CAT) {
68  bool Result = true;
69  size_t NumElems = CAT->getSize().getZExtValue();
70  QualType ElemType = CAT->getElementType();
71
72  if (ElemType->isRecordType()) {
73    const Record *R = BasePtr.getElemRecord();
74    for (size_t I = 0; I != NumElems; ++I) {
75      Pointer ElemPtr = BasePtr.atIndex(I).narrow();
76      Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R);
77    }
78  } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
79    for (size_t I = 0; I != NumElems; ++I) {
80      Pointer ElemPtr = BasePtr.atIndex(I).narrow();
81      Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT);
82    }
83  } else {
84    for (size_t I = 0; I != NumElems; ++I) {
85      if (!BasePtr.atIndex(I).isInitialized()) {
86        DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());
87        Result = false;
88      }
89    }
90  }
91
92  return Result;
93}
94
95static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
96                                   const Pointer &BasePtr, const Record *R) {
97  assert(R);
98  bool Result = true;
99  // Check all fields of this record are initialized.
100  for (const Record::Field &F : R->fields()) {
101    Pointer FieldPtr = BasePtr.atField(F.Offset);
102    QualType FieldType = F.Decl->getType();
103
104    if (FieldType->isRecordType()) {
105      Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord());
106    } else if (FieldType->isIncompleteArrayType()) {
107      // Nothing to do here.
108    } else if (FieldType->isArrayType()) {
109      const auto *CAT =
110          cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
111      Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT);
112    } else if (!FieldPtr.isInitialized()) {
113      DiagnoseUninitializedSubobject(S, Loc, F.Decl);
114      Result = false;
115    }
116  }
117
118  // Check Fields in all bases
119  for (const Record::Base &B : R->bases()) {
120    Pointer P = BasePtr.atField(B.Offset);
121    if (!P.isInitialized()) {
122      S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(),
123               diag::note_constexpr_uninitialized_base)
124          << B.Desc->getType();
125      return false;
126    }
127    Result &= CheckFieldsInitialized(S, Loc, P, B.R);
128  }
129
130  // TODO: Virtual bases
131
132  return Result;
133}
134
135bool EvaluationResult::checkFullyInitialized(InterpState &S) const {
136  assert(Source);
137  assert(isLValue());
138
139  // Our Source must be a VarDecl.
140  const Decl *SourceDecl = Source.dyn_cast<const Decl *>();
141  assert(SourceDecl);
142  const auto *VD = cast<VarDecl>(SourceDecl);
143  assert(VD->getType()->isRecordType() || VD->getType()->isArrayType());
144  SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc();
145
146  const Pointer &Ptr = *std::get_if<Pointer>(&Value);
147  assert(!Ptr.isZero());
148
149  if (const Record *R = Ptr.getRecord())
150    return CheckFieldsInitialized(S, InitLoc, Ptr, R);
151  const auto *CAT =
152      cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe());
153  return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
154
155  return true;
156}
157
158void EvaluationResult::dump() const {
159  assert(Ctx);
160  auto &OS = llvm::errs();
161  const ASTContext &ASTCtx = Ctx->getASTContext();
162
163  switch (Kind) {
164  case Empty:
165    OS << "Empty\n";
166    break;
167  case RValue:
168    OS << "RValue: ";
169    std::get<APValue>(Value).dump(OS, ASTCtx);
170    break;
171  case LValue: {
172    assert(Source);
173    QualType SourceType;
174    if (const auto *D = Source.dyn_cast<const Decl *>()) {
175      if (const auto *VD = dyn_cast<ValueDecl>(D))
176        SourceType = VD->getType();
177    } else if (const auto *E = Source.dyn_cast<const Expr *>()) {
178      SourceType = E->getType();
179    }
180
181    OS << "LValue: ";
182    if (const auto *P = std::get_if<Pointer>(&Value))
183      P->toAPValue().printPretty(OS, ASTCtx, SourceType);
184    else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
185      FP->toAPValue().printPretty(OS, ASTCtx, SourceType);
186    OS << "\n";
187    break;
188  }
189
190  default:
191    llvm_unreachable("Can't print that.");
192  }
193}
194
195} // namespace interp
196} // namespace clang
197