1353942Sdim//===--- State.cpp - State chain for the VM and AST Walker ------*- C++ -*-===//
2353942Sdim//
3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353942Sdim// See https://llvm.org/LICENSE.txt for license information.
5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353942Sdim//
7353942Sdim//===----------------------------------------------------------------------===//
8353942Sdim
9353942Sdim#include "State.h"
10353942Sdim#include "Frame.h"
11353942Sdim#include "Program.h"
12353942Sdim#include "clang/AST/ASTContext.h"
13353942Sdim#include "clang/AST/CXXInheritance.h"
14353942Sdim
15353942Sdimusing namespace clang;
16353942Sdimusing namespace clang::interp;
17353942Sdim
18353942SdimState::~State() {}
19353942Sdim
20353942SdimOptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
21353942Sdim                                 unsigned ExtraNotes) {
22353942Sdim  return diag(Loc, DiagId, ExtraNotes, false);
23353942Sdim}
24353942Sdim
25353942SdimOptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
26353942Sdim                                 unsigned ExtraNotes) {
27353942Sdim  if (getEvalStatus().Diag)
28353942Sdim    return diag(E->getExprLoc(), DiagId, ExtraNotes, false);
29353942Sdim  setActiveDiagnostic(false);
30353942Sdim  return OptionalDiagnostic();
31353942Sdim}
32353942Sdim
33353942SdimOptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId,
34353942Sdim                                 unsigned ExtraNotes) {
35353942Sdim  if (getEvalStatus().Diag)
36353942Sdim    return diag(SI.getLoc(), DiagId, ExtraNotes, false);
37353942Sdim  setActiveDiagnostic(false);
38353942Sdim  return OptionalDiagnostic();
39353942Sdim}
40353942Sdim
41353942SdimOptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
42353942Sdim                                  unsigned ExtraNotes) {
43353942Sdim  // Don't override a previous diagnostic. Don't bother collecting
44353942Sdim  // diagnostics if we're evaluating for overflow.
45353942Sdim  if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) {
46353942Sdim    setActiveDiagnostic(false);
47353942Sdim    return OptionalDiagnostic();
48353942Sdim  }
49353942Sdim  return diag(Loc, DiagId, ExtraNotes, true);
50353942Sdim}
51353942Sdim
52353942SdimOptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
53353942Sdim                                  unsigned ExtraNotes) {
54353942Sdim  return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
55353942Sdim}
56353942Sdim
57353942SdimOptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
58353942Sdim                                  unsigned ExtraNotes) {
59353942Sdim  return CCEDiag(SI.getLoc(), DiagId, ExtraNotes);
60353942Sdim}
61353942Sdim
62353942SdimOptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
63353942Sdim  if (!hasActiveDiagnostic())
64353942Sdim    return OptionalDiagnostic();
65353942Sdim  return OptionalDiagnostic(&addDiag(Loc, DiagId));
66353942Sdim}
67353942Sdim
68353942Sdimvoid State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
69353942Sdim  if (hasActiveDiagnostic()) {
70353942Sdim    getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(),
71353942Sdim                                 Diags.end());
72353942Sdim  }
73353942Sdim}
74353942Sdim
75353942SdimDiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
76353942Sdim  return getCtx().getDiagnostics().Report(Loc, DiagId);
77353942Sdim}
78353942Sdim
79353942Sdim/// Add a diagnostic to the diagnostics list.
80353942SdimPartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
81353942Sdim  PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator());
82353942Sdim  getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
83353942Sdim  return getEvalStatus().Diag->back().second;
84353942Sdim}
85353942Sdim
86353942SdimOptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
87353942Sdim                               unsigned ExtraNotes, bool IsCCEDiag) {
88353942Sdim  Expr::EvalStatus &EvalStatus = getEvalStatus();
89353942Sdim  if (EvalStatus.Diag) {
90353942Sdim    if (hasPriorDiagnostic()) {
91353942Sdim      return OptionalDiagnostic();
92353942Sdim    }
93353942Sdim
94353942Sdim    unsigned CallStackNotes = getCallStackDepth() - 1;
95353942Sdim    unsigned Limit = getCtx().getDiagnostics().getConstexprBacktraceLimit();
96353942Sdim    if (Limit)
97353942Sdim      CallStackNotes = std::min(CallStackNotes, Limit + 1);
98353942Sdim    if (checkingPotentialConstantExpression())
99353942Sdim      CallStackNotes = 0;
100353942Sdim
101353942Sdim    setActiveDiagnostic(true);
102353942Sdim    setFoldFailureDiagnostic(!IsCCEDiag);
103353942Sdim    EvalStatus.Diag->clear();
104353942Sdim    EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
105353942Sdim    addDiag(Loc, DiagId);
106353942Sdim    if (!checkingPotentialConstantExpression()) {
107353942Sdim      addCallStack(Limit);
108353942Sdim    }
109353942Sdim    return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
110353942Sdim  }
111353942Sdim  setActiveDiagnostic(false);
112353942Sdim  return OptionalDiagnostic();
113353942Sdim}
114353942Sdim
115353942Sdimconst LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); }
116353942Sdim
117353942Sdimvoid State::addCallStack(unsigned Limit) {
118353942Sdim  // Determine which calls to skip, if any.
119353942Sdim  unsigned ActiveCalls = getCallStackDepth() - 1;
120353942Sdim  unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
121353942Sdim  if (Limit && Limit < ActiveCalls) {
122353942Sdim    SkipStart = Limit / 2 + Limit % 2;
123353942Sdim    SkipEnd = ActiveCalls - Limit / 2;
124353942Sdim  }
125353942Sdim
126353942Sdim  // Walk the call stack and add the diagnostics.
127353942Sdim  unsigned CallIdx = 0;
128353942Sdim  Frame *Top = getCurrentFrame();
129353942Sdim  const Frame *Bottom = getBottomFrame();
130353942Sdim  for (Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) {
131353942Sdim    SourceLocation CallLocation = F->getCallLocation();
132353942Sdim
133353942Sdim    // Skip this call?
134353942Sdim    if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
135353942Sdim      if (CallIdx == SkipStart) {
136353942Sdim        // Note that we're skipping calls.
137353942Sdim        addDiag(CallLocation, diag::note_constexpr_calls_suppressed)
138353942Sdim            << unsigned(ActiveCalls - Limit);
139353942Sdim      }
140353942Sdim      continue;
141353942Sdim    }
142353942Sdim
143353942Sdim    // Use a different note for an inheriting constructor, because from the
144353942Sdim    // user's perspective it's not really a function at all.
145353942Sdim    if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(F->getCallee())) {
146353942Sdim      if (CD->isInheritingConstructor()) {
147353942Sdim        addDiag(CallLocation, diag::note_constexpr_inherited_ctor_call_here)
148353942Sdim            << CD->getParent();
149353942Sdim        continue;
150353942Sdim      }
151353942Sdim    }
152353942Sdim
153353942Sdim    SmallVector<char, 128> Buffer;
154353942Sdim    llvm::raw_svector_ostream Out(Buffer);
155353942Sdim    F->describe(Out);
156353942Sdim    addDiag(CallLocation, diag::note_constexpr_call_here) << Out.str();
157353942Sdim  }
158353942Sdim}
159