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 | llvm::append_range(C&: *getEvalStatus().Diag, R&: Diags); |
72 | } |
73 | |
74 | DiagnosticBuilder 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. |
79 | PartialDiagnostic &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 | |
85 | OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId, |
86 | unsigned , 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 | |
115 | const LangOptions &State::getLangOpts() const { |
116 | return getASTContext().getLangOpts(); |
117 | } |
118 | |
119 | void 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 | |