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 if (getEvalStatus().Diag)
29 return diag(Loc: E->getExprLoc(), DiagId, ExtraNotes, IsCCEDiag: false);
30 setActiveDiagnostic(false);
31 return OptionalDiagnostic();
32}
33
34OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId,
35 unsigned ExtraNotes) {
36 if (getEvalStatus().Diag)
37 return diag(Loc: SI.getLoc(), DiagId, ExtraNotes, IsCCEDiag: false);
38 setActiveDiagnostic(false);
39 return OptionalDiagnostic();
40}
41
42OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
43 unsigned ExtraNotes) {
44 // Don't override a previous diagnostic. Don't bother collecting
45 // diagnostics if we're evaluating for overflow.
46 if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) {
47 setActiveDiagnostic(false);
48 return OptionalDiagnostic();
49 }
50 return diag(Loc, DiagId, ExtraNotes, IsCCEDiag: true);
51}
52
53OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
54 unsigned ExtraNotes) {
55 return CCEDiag(Loc: E->getExprLoc(), DiagId, ExtraNotes);
56}
57
58OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
59 unsigned ExtraNotes) {
60 return CCEDiag(Loc: SI.getLoc(), DiagId, ExtraNotes);
61}
62
63OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
64 if (!hasActiveDiagnostic())
65 return OptionalDiagnostic();
66 return OptionalDiagnostic(&addDiag(Loc, DiagId));
67}
68
69void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
70 if (hasActiveDiagnostic())
71 llvm::append_range(C&: *getEvalStatus().Diag, R&: Diags);
72}
73
74DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
75 return getASTContext().getDiagnostics().Report(Loc, DiagID: DiagId);
76}
77
78/// Add a diagnostic to the diagnostics list.
79PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
80 PartialDiagnostic PD(DiagId, getASTContext().getDiagAllocator());
81 getEvalStatus().Diag->push_back(Elt: std::make_pair(x&: Loc, y&: PD));
82 return getEvalStatus().Diag->back().second;
83}
84
85OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
86 unsigned ExtraNotes, bool IsCCEDiag) {
87 Expr::EvalStatus &EvalStatus = getEvalStatus();
88 if (EvalStatus.Diag) {
89 if (hasPriorDiagnostic()) {
90 return OptionalDiagnostic();
91 }
92
93 unsigned CallStackNotes = getCallStackDepth() - 1;
94 unsigned Limit =
95 getASTContext().getDiagnostics().getConstexprBacktraceLimit();
96 if (Limit)
97 CallStackNotes = std::min(a: CallStackNotes, b: Limit + 1);
98 if (checkingPotentialConstantExpression())
99 CallStackNotes = 0;
100
101 setActiveDiagnostic(true);
102 setFoldFailureDiagnostic(!IsCCEDiag);
103 EvalStatus.Diag->clear();
104 EvalStatus.Diag->reserve(N: 1 + ExtraNotes + CallStackNotes);
105 addDiag(Loc, DiagId);
106 if (!checkingPotentialConstantExpression()) {
107 addCallStack(Limit);
108 }
109 return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
110 }
111 setActiveDiagnostic(false);
112 return OptionalDiagnostic();
113}
114
115const LangOptions &State::getLangOpts() const {
116 return getASTContext().getLangOpts();
117}
118
119void State::addCallStack(unsigned Limit) {
120 // Determine which calls to skip, if any.
121 unsigned ActiveCalls = getCallStackDepth() - 1;
122 unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
123 if (Limit && Limit < ActiveCalls) {
124 SkipStart = Limit / 2 + Limit % 2;
125 SkipEnd = ActiveCalls - Limit / 2;
126 }
127
128 // Walk the call stack and add the diagnostics.
129 unsigned CallIdx = 0;
130 const Frame *Top = getCurrentFrame();
131 const Frame *Bottom = getBottomFrame();
132 for (const Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) {
133 SourceRange CallRange = F->getCallRange();
134
135 // Skip this call?
136 if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
137 if (CallIdx == SkipStart) {
138 // Note that we're skipping calls.
139 addDiag(Loc: CallRange.getBegin(), DiagId: diag::note_constexpr_calls_suppressed)
140 << unsigned(ActiveCalls - Limit);
141 }
142 continue;
143 }
144
145 // Use a different note for an inheriting constructor, because from the
146 // user's perspective it's not really a function at all.
147 if (const auto *CD =
148 dyn_cast_if_present<CXXConstructorDecl>(Val: F->getCallee());
149 CD && CD->isInheritingConstructor()) {
150 addDiag(Loc: CallRange.getBegin(),
151 DiagId: diag::note_constexpr_inherited_ctor_call_here)
152 << CD->getParent();
153 continue;
154 }
155
156 SmallString<128> Buffer;
157 llvm::raw_svector_ostream Out(Buffer);
158 F->describe(OS&: Out);
159 if (!Buffer.empty())
160 addDiag(Loc: CallRange.getBegin(), DiagId: diag::note_constexpr_call_here)
161 << Out.str() << CallRange;
162 }
163}
164