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