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