xref: /llvm-project/clang/lib/AST/ByteCode/State.cpp (revision d9e728601938f7d587ac580d32f042fa74041864)
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 ExtraNotes) {
23   return diag(Loc, DiagId, ExtraNotes, false);
24 }
25 
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 
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 
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 
53 OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
54                                   unsigned ExtraNotes) {
55   return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
56 }
57 
58 OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
59                                   unsigned ExtraNotes) {
60   return CCEDiag(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     getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(),
72                                  Diags.end());
73   }
74 }
75 
76 DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
77   return getASTContext().getDiagnostics().Report(Loc, DiagId);
78 }
79 
80 /// Add a diagnostic to the diagnostics list.
81 PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
82   PartialDiagnostic PD(DiagId, getASTContext().getDiagAllocator());
83   getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
84   return getEvalStatus().Diag->back().second;
85 }
86 
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 =
97         getASTContext().getDiagnostics().getConstexprBacktraceLimit();
98     if (Limit)
99       CallStackNotes = std::min(CallStackNotes, Limit + 1);
100     if (checkingPotentialConstantExpression())
101       CallStackNotes = 0;
102 
103     setActiveDiagnostic(true);
104     setFoldFailureDiagnostic(!IsCCEDiag);
105     EvalStatus.Diag->clear();
106     EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
107     addDiag(Loc, DiagId);
108     if (!checkingPotentialConstantExpression()) {
109       addCallStack(Limit);
110     }
111     return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
112   }
113   setActiveDiagnostic(false);
114   return OptionalDiagnostic();
115 }
116 
117 const LangOptions &State::getLangOpts() const {
118   return getASTContext().getLangOpts();
119 }
120 
121 void State::addCallStack(unsigned Limit) {
122   // Determine which calls to skip, if any.
123   unsigned ActiveCalls = getCallStackDepth() - 1;
124   unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
125   if (Limit && Limit < ActiveCalls) {
126     SkipStart = Limit / 2 + Limit % 2;
127     SkipEnd = ActiveCalls - Limit / 2;
128   }
129 
130   // Walk the call stack and add the diagnostics.
131   unsigned CallIdx = 0;
132   const Frame *Top = getCurrentFrame();
133   const Frame *Bottom = getBottomFrame();
134   for (const Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) {
135     SourceRange CallRange = F->getCallRange();
136 
137     // Skip this call?
138     if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
139       if (CallIdx == SkipStart) {
140         // Note that we're skipping calls.
141         addDiag(CallRange.getBegin(), diag::note_constexpr_calls_suppressed)
142             << unsigned(ActiveCalls - Limit);
143       }
144       continue;
145     }
146 
147     // Use a different note for an inheriting constructor, because from the
148     // user's perspective it's not really a function at all.
149     if (const auto *CD =
150             dyn_cast_if_present<CXXConstructorDecl>(F->getCallee());
151         CD && CD->isInheritingConstructor()) {
152       addDiag(CallRange.getBegin(),
153               diag::note_constexpr_inherited_ctor_call_here)
154           << CD->getParent();
155       continue;
156     }
157 
158     SmallString<128> Buffer;
159     llvm::raw_svector_ostream Out(Buffer);
160     F->describe(Out);
161     if (!Buffer.empty())
162       addDiag(CallRange.getBegin(), diag::note_constexpr_call_here)
163           << Out.str() << CallRange;
164   }
165 }
166