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