1//===--- State.cpp - State chain for the VM and AST Walker ------*- 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 "State.h"
10#include "Frame.h"
11#include "Program.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/AST/CXXInheritance.h"
14#include "clang/AST/OptionalDiagnostic.h"
15
16using namespace clang;
17using namespace clang::interp;
18
19State::~State() {}
20
21OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
22 unsigned ExtraNotes) {
23 return diag(Loc, DiagId, ExtraNotes, IsCCEDiag: false);
24}
25
26OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
27 unsigned ExtraNotes) {
28 EvalStatus.DiagEmitted = true;
29 if (EvalStatus.Diag)
30 return diag(Loc: E->getExprLoc(), DiagId, ExtraNotes, IsCCEDiag: false);
31 setActiveDiagnostic(false);
32 return OptionalDiagnostic();
33}
34
35OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind DiagId,
36 unsigned ExtraNotes) {
37 EvalStatus.DiagEmitted = true;
38 if (EvalStatus.Diag)
39 return diag(Loc: SI.getLoc(), DiagId, ExtraNotes, IsCCEDiag: false);
40 setActiveDiagnostic(false);
41 return OptionalDiagnostic();
42}
43
44OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
45 unsigned ExtraNotes) {
46 EvalStatus.DiagEmitted = true;
47 // Don't override a previous diagnostic. Don't bother collecting
48 // diagnostics if we're evaluating for overflow.
49 if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) {
50 setActiveDiagnostic(false);
51 return OptionalDiagnostic();
52 }
53 return diag(Loc, DiagId, ExtraNotes, IsCCEDiag: true);
54}
55
56OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
57 unsigned ExtraNotes) {
58 return CCEDiag(Loc: E->getExprLoc(), DiagId, ExtraNotes);
59}
60
61OptionalDiagnostic State::CCEDiag(SourceInfo SI, diag::kind DiagId,
62 unsigned ExtraNotes) {
63 return CCEDiag(Loc: SI.getLoc(), DiagId, ExtraNotes);
64}
65
66OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
67 if (!hasActiveDiagnostic())
68 return OptionalDiagnostic();
69 return OptionalDiagnostic(&addDiag(Loc, DiagId));
70}
71
72void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
73 if (hasActiveDiagnostic())
74 llvm::append_range(C&: *EvalStatus.Diag, R&: Diags);
75}
76
77DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
78 return Ctx.getDiagnostics().Report(Loc, DiagID: DiagId);
79}
80
81/// Add a diagnostic to the diagnostics list.
82PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
83 PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
84 EvalStatus.Diag->push_back(Elt: std::make_pair(x&: Loc, y&: PD));
85 return EvalStatus.Diag->back().second;
86}
87
88OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
89 unsigned ExtraNotes, bool IsCCEDiag) {
90 if (EvalStatus.Diag) {
91 if (hasPriorDiagnostic()) {
92 return OptionalDiagnostic();
93 }
94
95 unsigned CallStackNotes = getCallStackDepth() - 1;
96 unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit();
97 if (Limit)
98 CallStackNotes = std::min(a: CallStackNotes, b: Limit + 1);
99 if (checkingPotentialConstantExpression())
100 CallStackNotes = 0;
101
102 setActiveDiagnostic(true);
103 setFoldFailureDiagnostic(!IsCCEDiag);
104 EvalStatus.Diag->clear();
105 EvalStatus.Diag->reserve(N: 1 + ExtraNotes + CallStackNotes);
106 addDiag(Loc, DiagId);
107 if (!checkingPotentialConstantExpression()) {
108 addCallStack(Limit);
109 }
110 return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
111 }
112 setActiveDiagnostic(false);
113 return OptionalDiagnostic();
114}
115
116void State::addCallStack(unsigned Limit) {
117 // Determine which calls to skip, if any.
118 unsigned ActiveCalls = getCallStackDepth() - 1;
119 unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
120 if (Limit && Limit < ActiveCalls) {
121 SkipStart = Limit / 2 + Limit % 2;
122 SkipEnd = ActiveCalls - Limit / 2;
123 }
124
125 // Walk the call stack and add the diagnostics.
126 unsigned CallIdx = 0;
127 const Frame *Top = getCurrentFrame();
128 for (const Frame *F = Top; F->getCaller() != nullptr;
129 F = F->getCaller(), ++CallIdx) {
130 SourceRange CallRange = F->getCallRange();
131 assert(CallRange.isValid());
132
133 // Skip this call?
134 if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
135 if (CallIdx == SkipStart) {
136 // Note that we're skipping calls.
137 addDiag(Loc: CallRange.getBegin(), DiagId: diag::note_constexpr_calls_suppressed)
138 << unsigned(ActiveCalls - Limit);
139 }
140 continue;
141 }
142
143 // Use a different note for an inheriting constructor, because from the
144 // user's perspective it's not really a function at all.
145 if (const auto *CD =
146 dyn_cast_if_present<CXXConstructorDecl>(Val: F->getCallee());
147 CD && CD->isInheritingConstructor()) {
148 addDiag(Loc: CallRange.getBegin(),
149 DiagId: diag::note_constexpr_inherited_ctor_call_here)
150 << CD->getParent();
151 continue;
152 }
153
154 SmallString<128> Buffer;
155 llvm::raw_svector_ostream Out(Buffer);
156 F->describe(OS&: Out);
157 if (!Buffer.empty())
158 addDiag(Loc: CallRange.getBegin(), DiagId: diag::note_constexpr_call_here)
159 << Out.str() << CallRange;
160 }
161}
162
163bool State::hasPriorDiagnostic() {
164 if (!EvalStatus.Diag->empty()) {
165 switch (EvalMode) {
166 case EvaluationMode::ConstantFold:
167 case EvaluationMode::IgnoreSideEffects:
168 if (!HasFoldFailureDiagnostic)
169 break;
170 // We've already failed to fold something. Keep that diagnostic.
171 [[fallthrough]];
172 case EvaluationMode::ConstantExpression:
173 case EvaluationMode::ConstantExpressionUnevaluated:
174 setActiveDiagnostic(false);
175 return true;
176 }
177 }
178 return false;
179}
180
181bool State::keepEvaluatingAfterFailure() const {
182 uint64_t Limit = Ctx.getLangOpts().ConstexprStepLimit;
183 if (Limit != 0 && !stepsLeft())
184 return false;
185
186 switch (EvalMode) {
187 case EvaluationMode::ConstantExpression:
188 case EvaluationMode::ConstantExpressionUnevaluated:
189 case EvaluationMode::ConstantFold:
190 case EvaluationMode::IgnoreSideEffects:
191 return checkingPotentialConstantExpression() ||
192 checkingForUndefinedBehavior();
193 }
194 llvm_unreachable("Missed EvalMode case");
195}
196
197bool State::keepEvaluatingAfterSideEffect() const {
198 switch (EvalMode) {
199 case EvaluationMode::IgnoreSideEffects:
200 return true;
201
202 case EvaluationMode::ConstantExpression:
203 case EvaluationMode::ConstantExpressionUnevaluated:
204 case EvaluationMode::ConstantFold:
205 // By default, assume any side effect might be valid in some other
206 // evaluation of this expression from a different context.
207 return checkingPotentialConstantExpression() ||
208 checkingForUndefinedBehavior();
209 }
210 llvm_unreachable("Missed EvalMode case");
211}
212
213bool State::keepEvaluatingAfterUndefinedBehavior() const {
214 switch (EvalMode) {
215 case EvaluationMode::IgnoreSideEffects:
216 case EvaluationMode::ConstantFold:
217 return true;
218
219 case EvaluationMode::ConstantExpression:
220 case EvaluationMode::ConstantExpressionUnevaluated:
221 return checkingForUndefinedBehavior();
222 }
223 llvm_unreachable("Missed EvalMode case");
224}
225