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
~State()19 State::~State() {}
20
FFDiag(SourceLocation Loc,diag::kind DiagId,unsigned ExtraNotes)21 OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
22 unsigned ExtraNotes) {
23 return diag(Loc, DiagId, ExtraNotes, false);
24 }
25
FFDiag(const Expr * E,diag::kind DiagId,unsigned ExtraNotes)26 OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
27 unsigned ExtraNotes) {
28 if (getEvalStatus().Diag)
29 return diag(E->getExprLoc(), DiagId, ExtraNotes, false);
30 setActiveDiagnostic(false);
31 return OptionalDiagnostic();
32 }
33
FFDiag(const SourceInfo & SI,diag::kind DiagId,unsigned ExtraNotes)34 OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId,
35 unsigned ExtraNotes) {
36 if (getEvalStatus().Diag)
37 return diag(SI.getLoc(), DiagId, ExtraNotes, false);
38 setActiveDiagnostic(false);
39 return OptionalDiagnostic();
40 }
41
CCEDiag(SourceLocation Loc,diag::kind DiagId,unsigned ExtraNotes)42 OptionalDiagnostic 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, true);
51 }
52
CCEDiag(const Expr * E,diag::kind DiagId,unsigned ExtraNotes)53 OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
54 unsigned ExtraNotes) {
55 return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
56 }
57
CCEDiag(const SourceInfo & SI,diag::kind DiagId,unsigned ExtraNotes)58 OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
59 unsigned ExtraNotes) {
60 return CCEDiag(SI.getLoc(), DiagId, ExtraNotes);
61 }
62
Note(SourceLocation Loc,diag::kind DiagId)63 OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
64 if (!hasActiveDiagnostic())
65 return OptionalDiagnostic();
66 return OptionalDiagnostic(&addDiag(Loc, DiagId));
67 }
68
addNotes(ArrayRef<PartialDiagnosticAt> Diags)69 void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
70 if (hasActiveDiagnostic()) {
71 getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(),
72 Diags.end());
73 }
74 }
75
report(SourceLocation Loc,diag::kind DiagId)76 DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
77 return getCtx().getDiagnostics().Report(Loc, DiagId);
78 }
79
80 /// Add a diagnostic to the diagnostics list.
addDiag(SourceLocation Loc,diag::kind DiagId)81 PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
82 PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator());
83 getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
84 return getEvalStatus().Diag->back().second;
85 }
86
diag(SourceLocation Loc,diag::kind DiagId,unsigned ExtraNotes,bool IsCCEDiag)87 OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
88 unsigned ExtraNotes, 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(CallStackNotes, Limit + 1);
99 if (checkingPotentialConstantExpression())
100 CallStackNotes = 0;
101
102 setActiveDiagnostic(true);
103 setFoldFailureDiagnostic(!IsCCEDiag);
104 EvalStatus.Diag->clear();
105 EvalStatus.Diag->reserve(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
getLangOpts() const116 const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); }
117
addCallStack(unsigned Limit)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>(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(Out);
158 if (!Buffer.empty())
159 addDiag(CallRange.getBegin(), diag::note_constexpr_call_here)
160 << Out.str() << CallRange;
161 }
162 }
163