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 getCtx().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, getCtx().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 = 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 116 const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); } 117 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 SourceLocation CallLocation = F->getCallLocation(); 133 134 // Skip this call? 135 if (CallIdx >= SkipStart && CallIdx < SkipEnd) { 136 if (CallIdx == SkipStart) { 137 // Note that we're skipping calls. 138 addDiag(CallLocation, 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(CallLocation, diag::note_constexpr_inherited_ctor_call_here) 150 << CD->getParent(); 151 continue; 152 } 153 154 SmallString<128> Buffer; 155 llvm::raw_svector_ostream Out(Buffer); 156 F->describe(Out); 157 addDiag(CallLocation, diag::note_constexpr_call_here) << Out.str(); 158 } 159 } 160