xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/State.cpp (revision ec0ea6efa1ad229d75c394c1a9b9cac33af2b1d3)
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 
15 using namespace clang;
16 using namespace clang::interp;
17 
18 State::~State() {}
19 
20 OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
21                                  unsigned ExtraNotes) {
22   return diag(Loc, DiagId, ExtraNotes, false);
23 }
24 
25 OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
26                                  unsigned ExtraNotes) {
27   if (getEvalStatus().Diag)
28     return diag(E->getExprLoc(), DiagId, ExtraNotes, false);
29   setActiveDiagnostic(false);
30   return OptionalDiagnostic();
31 }
32 
33 OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId,
34                                  unsigned ExtraNotes) {
35   if (getEvalStatus().Diag)
36     return diag(SI.getLoc(), DiagId, ExtraNotes, false);
37   setActiveDiagnostic(false);
38   return OptionalDiagnostic();
39 }
40 
41 OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
42                                   unsigned ExtraNotes) {
43   // Don't override a previous diagnostic. Don't bother collecting
44   // diagnostics if we're evaluating for overflow.
45   if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) {
46     setActiveDiagnostic(false);
47     return OptionalDiagnostic();
48   }
49   return diag(Loc, DiagId, ExtraNotes, true);
50 }
51 
52 OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
53                                   unsigned ExtraNotes) {
54   return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
55 }
56 
57 OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
58                                   unsigned ExtraNotes) {
59   return CCEDiag(SI.getLoc(), DiagId, ExtraNotes);
60 }
61 
62 OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
63   if (!hasActiveDiagnostic())
64     return OptionalDiagnostic();
65   return OptionalDiagnostic(&addDiag(Loc, DiagId));
66 }
67 
68 void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
69   if (hasActiveDiagnostic()) {
70     getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(),
71                                  Diags.end());
72   }
73 }
74 
75 DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
76   return getCtx().getDiagnostics().Report(Loc, DiagId);
77 }
78 
79 /// Add a diagnostic to the diagnostics list.
80 PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
81   PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator());
82   getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
83   return getEvalStatus().Diag->back().second;
84 }
85 
86 OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
87                                unsigned ExtraNotes, bool IsCCEDiag) {
88   Expr::EvalStatus &EvalStatus = getEvalStatus();
89   if (EvalStatus.Diag) {
90     if (hasPriorDiagnostic()) {
91       return OptionalDiagnostic();
92     }
93 
94     unsigned CallStackNotes = getCallStackDepth() - 1;
95     unsigned Limit = getCtx().getDiagnostics().getConstexprBacktraceLimit();
96     if (Limit)
97       CallStackNotes = std::min(CallStackNotes, Limit + 1);
98     if (checkingPotentialConstantExpression())
99       CallStackNotes = 0;
100 
101     setActiveDiagnostic(true);
102     setFoldFailureDiagnostic(!IsCCEDiag);
103     EvalStatus.Diag->clear();
104     EvalStatus.Diag->reserve(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 { return getCtx().getLangOpts(); }
116 
117 void State::addCallStack(unsigned Limit) {
118   // Determine which calls to skip, if any.
119   unsigned ActiveCalls = getCallStackDepth() - 1;
120   unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
121   if (Limit && Limit < ActiveCalls) {
122     SkipStart = Limit / 2 + Limit % 2;
123     SkipEnd = ActiveCalls - Limit / 2;
124   }
125 
126   // Walk the call stack and add the diagnostics.
127   unsigned CallIdx = 0;
128   Frame *Top = getCurrentFrame();
129   const Frame *Bottom = getBottomFrame();
130   for (Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) {
131     SourceLocation CallLocation = F->getCallLocation();
132 
133     // Skip this call?
134     if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
135       if (CallIdx == SkipStart) {
136         // Note that we're skipping calls.
137         addDiag(CallLocation, diag::note_constexpr_calls_suppressed)
138             << unsigned(ActiveCalls - Limit);
139       }
140       continue;
141     }
142 
143     // Use a different note for an inheriting constructor, because from the
144     // user's perspective it's not really a function at all.
145     if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(F->getCallee())) {
146       if (CD->isInheritingConstructor()) {
147         addDiag(CallLocation, diag::note_constexpr_inherited_ctor_call_here)
148             << CD->getParent();
149         continue;
150       }
151     }
152 
153     SmallString<128> Buffer;
154     llvm::raw_svector_ostream Out(Buffer);
155     F->describe(Out);
156     addDiag(CallLocation, diag::note_constexpr_call_here) << Out.str();
157   }
158 }
159