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 | State::~State() {} |
20 | |
21 | OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId, |
22 | unsigned ) { |
23 | return diag(Loc, DiagId, ExtraNotes, IsCCEDiag: false); |
24 | } |
25 | |
26 | OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId, |
27 | unsigned ) { |
28 | if (getEvalStatus().Diag) |
29 | return diag(Loc: E->getExprLoc(), DiagId, ExtraNotes, IsCCEDiag: false); |
30 | setActiveDiagnostic(false); |
31 | return OptionalDiagnostic(); |
32 | } |
33 | |
34 | OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId, |
35 | unsigned ) { |
36 | if (getEvalStatus().Diag) |
37 | return diag(Loc: SI.getLoc(), DiagId, ExtraNotes, IsCCEDiag: false); |
38 | setActiveDiagnostic(false); |
39 | return OptionalDiagnostic(); |
40 | } |
41 | |
42 | OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId, |
43 | unsigned ) { |
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 | |
53 | OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId, |
54 | unsigned ) { |
55 | return CCEDiag(Loc: E->getExprLoc(), DiagId, ExtraNotes); |
56 | } |
57 | |
58 | OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId, |
59 | unsigned ) { |
60 | return CCEDiag(Loc: SI.getLoc(), DiagId, ExtraNotes); |
61 | } |
62 | |
63 | OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) { |
64 | if (!hasActiveDiagnostic()) |
65 | return OptionalDiagnostic(); |
66 | return OptionalDiagnostic(&addDiag(Loc, DiagId)); |
67 | } |
68 | |
69 | void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) { |
70 | if (hasActiveDiagnostic()) { |
71 | getEvalStatus().Diag->insert(I: getEvalStatus().Diag->end(), From: Diags.begin(), |
72 | To: Diags.end()); |
73 | } |
74 | } |
75 | |
76 | DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) { |
77 | return getCtx().getDiagnostics().Report(Loc, DiagID: DiagId); |
78 | } |
79 | |
80 | /// Add a diagnostic to the diagnostics list. |
81 | PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) { |
82 | PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator()); |
83 | getEvalStatus().Diag->push_back(Elt: std::make_pair(x&: Loc, y&: PD)); |
84 | return getEvalStatus().Diag->back().second; |
85 | } |
86 | |
87 | OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId, |
88 | unsigned , bool IsCCEDiag) { |
89 | Expr::EvalStatus &EvalStatus = getEvalStatus(); |
90 | if (EvalStatus.Diag) { |
91 | if (hasPriorDiagnostic()) { |
92 | return OptionalDiagnostic(); |
93 | } |
94 | |
95 | unsigned CallStackNotes = getCallStackDepth() - 1; |
96 | unsigned Limit = getCtx().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 | |
116 | const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); } |
117 | |
118 | void State::addCallStack(unsigned Limit) { |
119 | // Determine which calls to skip, if any. |
120 | unsigned ActiveCalls = getCallStackDepth() - 1; |
121 | unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; |
122 | if (Limit && Limit < ActiveCalls) { |
123 | SkipStart = Limit / 2 + Limit % 2; |
124 | SkipEnd = ActiveCalls - Limit / 2; |
125 | } |
126 | |
127 | // Walk the call stack and add the diagnostics. |
128 | unsigned CallIdx = 0; |
129 | const Frame *Top = getCurrentFrame(); |
130 | const Frame *Bottom = getBottomFrame(); |
131 | for (const Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) { |
132 | SourceRange CallRange = F->getCallRange(); |
133 | |
134 | // Skip this call? |
135 | if (CallIdx >= SkipStart && CallIdx < SkipEnd) { |
136 | if (CallIdx == SkipStart) { |
137 | // Note that we're skipping calls. |
138 | addDiag(CallRange.getBegin(), diag::note_constexpr_calls_suppressed) |
139 | << unsigned(ActiveCalls - Limit); |
140 | } |
141 | continue; |
142 | } |
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 | if (const auto *CD = |
147 | dyn_cast_if_present<CXXConstructorDecl>(Val: F->getCallee()); |
148 | CD && CD->isInheritingConstructor()) { |
149 | addDiag(CallRange.getBegin(), |
150 | diag::note_constexpr_inherited_ctor_call_here) |
151 | << CD->getParent(); |
152 | continue; |
153 | } |
154 | |
155 | SmallString<128> Buffer; |
156 | llvm::raw_svector_ostream Out(Buffer); |
157 | F->describe(OS&: Out); |
158 | if (!Buffer.empty()) |
159 | addDiag(CallRange.getBegin(), diag::note_constexpr_call_here) |
160 | << Out.str() << CallRange; |
161 | } |
162 | } |
163 | |