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