1a7dea167SDimitry Andric //===--- State.cpp - State chain for the VM and AST Walker ------*- C++ -*-===// 2a7dea167SDimitry Andric // 3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a7dea167SDimitry Andric // 7a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8a7dea167SDimitry Andric 9a7dea167SDimitry Andric #include "State.h" 10a7dea167SDimitry Andric #include "Frame.h" 11a7dea167SDimitry Andric #include "Program.h" 12a7dea167SDimitry Andric #include "clang/AST/ASTContext.h" 13a7dea167SDimitry Andric #include "clang/AST/CXXInheritance.h" 1406c3fb27SDimitry Andric #include "clang/AST/OptionalDiagnostic.h" 15a7dea167SDimitry Andric 16a7dea167SDimitry Andric using namespace clang; 17a7dea167SDimitry Andric using namespace clang::interp; 18a7dea167SDimitry Andric 19a7dea167SDimitry Andric State::~State() {} 20a7dea167SDimitry Andric 21a7dea167SDimitry Andric OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId, 22a7dea167SDimitry Andric unsigned ExtraNotes) { 23a7dea167SDimitry Andric return diag(Loc, DiagId, ExtraNotes, false); 24a7dea167SDimitry Andric } 25a7dea167SDimitry Andric 26a7dea167SDimitry Andric OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId, 27a7dea167SDimitry Andric unsigned ExtraNotes) { 28a7dea167SDimitry Andric if (getEvalStatus().Diag) 29a7dea167SDimitry Andric return diag(E->getExprLoc(), DiagId, ExtraNotes, false); 30a7dea167SDimitry Andric setActiveDiagnostic(false); 31a7dea167SDimitry Andric return OptionalDiagnostic(); 32a7dea167SDimitry Andric } 33a7dea167SDimitry Andric 34a7dea167SDimitry Andric OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId, 35a7dea167SDimitry Andric unsigned ExtraNotes) { 36a7dea167SDimitry Andric if (getEvalStatus().Diag) 37a7dea167SDimitry Andric return diag(SI.getLoc(), DiagId, ExtraNotes, false); 38a7dea167SDimitry Andric setActiveDiagnostic(false); 39a7dea167SDimitry Andric return OptionalDiagnostic(); 40a7dea167SDimitry Andric } 41a7dea167SDimitry Andric 42a7dea167SDimitry Andric OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId, 43a7dea167SDimitry Andric unsigned ExtraNotes) { 44a7dea167SDimitry Andric // Don't override a previous diagnostic. Don't bother collecting 45a7dea167SDimitry Andric // diagnostics if we're evaluating for overflow. 46a7dea167SDimitry Andric if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) { 47a7dea167SDimitry Andric setActiveDiagnostic(false); 48a7dea167SDimitry Andric return OptionalDiagnostic(); 49a7dea167SDimitry Andric } 50a7dea167SDimitry Andric return diag(Loc, DiagId, ExtraNotes, true); 51a7dea167SDimitry Andric } 52a7dea167SDimitry Andric 53a7dea167SDimitry Andric OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId, 54a7dea167SDimitry Andric unsigned ExtraNotes) { 55a7dea167SDimitry Andric return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes); 56a7dea167SDimitry Andric } 57a7dea167SDimitry Andric 58a7dea167SDimitry Andric OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId, 59a7dea167SDimitry Andric unsigned ExtraNotes) { 60a7dea167SDimitry Andric return CCEDiag(SI.getLoc(), DiagId, ExtraNotes); 61a7dea167SDimitry Andric } 62a7dea167SDimitry Andric 63a7dea167SDimitry Andric OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) { 64a7dea167SDimitry Andric if (!hasActiveDiagnostic()) 65a7dea167SDimitry Andric return OptionalDiagnostic(); 66a7dea167SDimitry Andric return OptionalDiagnostic(&addDiag(Loc, DiagId)); 67a7dea167SDimitry Andric } 68a7dea167SDimitry Andric 69a7dea167SDimitry Andric void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) { 70a7dea167SDimitry Andric if (hasActiveDiagnostic()) { 71a7dea167SDimitry Andric getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(), 72a7dea167SDimitry Andric Diags.end()); 73a7dea167SDimitry Andric } 74a7dea167SDimitry Andric } 75a7dea167SDimitry Andric 76a7dea167SDimitry Andric DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) { 77a7dea167SDimitry Andric return getCtx().getDiagnostics().Report(Loc, DiagId); 78a7dea167SDimitry Andric } 79a7dea167SDimitry Andric 80a7dea167SDimitry Andric /// Add a diagnostic to the diagnostics list. 81a7dea167SDimitry Andric PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) { 82a7dea167SDimitry Andric PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator()); 83a7dea167SDimitry Andric getEvalStatus().Diag->push_back(std::make_pair(Loc, PD)); 84a7dea167SDimitry Andric return getEvalStatus().Diag->back().second; 85a7dea167SDimitry Andric } 86a7dea167SDimitry Andric 87a7dea167SDimitry Andric OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId, 88a7dea167SDimitry Andric unsigned ExtraNotes, bool IsCCEDiag) { 89a7dea167SDimitry Andric Expr::EvalStatus &EvalStatus = getEvalStatus(); 90a7dea167SDimitry Andric if (EvalStatus.Diag) { 91a7dea167SDimitry Andric if (hasPriorDiagnostic()) { 92a7dea167SDimitry Andric return OptionalDiagnostic(); 93a7dea167SDimitry Andric } 94a7dea167SDimitry Andric 95a7dea167SDimitry Andric unsigned CallStackNotes = getCallStackDepth() - 1; 96a7dea167SDimitry Andric unsigned Limit = getCtx().getDiagnostics().getConstexprBacktraceLimit(); 97a7dea167SDimitry Andric if (Limit) 98a7dea167SDimitry Andric CallStackNotes = std::min(CallStackNotes, Limit + 1); 99a7dea167SDimitry Andric if (checkingPotentialConstantExpression()) 100a7dea167SDimitry Andric CallStackNotes = 0; 101a7dea167SDimitry Andric 102a7dea167SDimitry Andric setActiveDiagnostic(true); 103a7dea167SDimitry Andric setFoldFailureDiagnostic(!IsCCEDiag); 104a7dea167SDimitry Andric EvalStatus.Diag->clear(); 105a7dea167SDimitry Andric EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes); 106a7dea167SDimitry Andric addDiag(Loc, DiagId); 107a7dea167SDimitry Andric if (!checkingPotentialConstantExpression()) { 108a7dea167SDimitry Andric addCallStack(Limit); 109a7dea167SDimitry Andric } 110a7dea167SDimitry Andric return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second); 111a7dea167SDimitry Andric } 112a7dea167SDimitry Andric setActiveDiagnostic(false); 113a7dea167SDimitry Andric return OptionalDiagnostic(); 114a7dea167SDimitry Andric } 115a7dea167SDimitry Andric 116a7dea167SDimitry Andric const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); } 117a7dea167SDimitry Andric 118a7dea167SDimitry Andric void State::addCallStack(unsigned Limit) { 119a7dea167SDimitry Andric // Determine which calls to skip, if any. 120a7dea167SDimitry Andric unsigned ActiveCalls = getCallStackDepth() - 1; 121a7dea167SDimitry Andric unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; 122a7dea167SDimitry Andric if (Limit && Limit < ActiveCalls) { 123a7dea167SDimitry Andric SkipStart = Limit / 2 + Limit % 2; 124a7dea167SDimitry Andric SkipEnd = ActiveCalls - Limit / 2; 125a7dea167SDimitry Andric } 126a7dea167SDimitry Andric 127a7dea167SDimitry Andric // Walk the call stack and add the diagnostics. 128a7dea167SDimitry Andric unsigned CallIdx = 0; 12906c3fb27SDimitry Andric const Frame *Top = getCurrentFrame(); 130a7dea167SDimitry Andric const Frame *Bottom = getBottomFrame(); 13106c3fb27SDimitry Andric for (const Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) { 1325f757f3fSDimitry Andric SourceRange CallRange = F->getCallRange(); 133a7dea167SDimitry Andric 134a7dea167SDimitry Andric // Skip this call? 135a7dea167SDimitry Andric if (CallIdx >= SkipStart && CallIdx < SkipEnd) { 136a7dea167SDimitry Andric if (CallIdx == SkipStart) { 137a7dea167SDimitry Andric // Note that we're skipping calls. 1385f757f3fSDimitry Andric addDiag(CallRange.getBegin(), diag::note_constexpr_calls_suppressed) 139a7dea167SDimitry Andric << unsigned(ActiveCalls - Limit); 140a7dea167SDimitry Andric } 141a7dea167SDimitry Andric continue; 142a7dea167SDimitry Andric } 143a7dea167SDimitry Andric 144a7dea167SDimitry Andric // Use a different note for an inheriting constructor, because from the 145a7dea167SDimitry Andric // user's perspective it's not really a function at all. 14606c3fb27SDimitry Andric if (const auto *CD = 14706c3fb27SDimitry Andric dyn_cast_if_present<CXXConstructorDecl>(F->getCallee()); 14806c3fb27SDimitry Andric CD && CD->isInheritingConstructor()) { 1495f757f3fSDimitry Andric addDiag(CallRange.getBegin(), 1505f757f3fSDimitry Andric diag::note_constexpr_inherited_ctor_call_here) 151a7dea167SDimitry Andric << CD->getParent(); 152a7dea167SDimitry Andric continue; 153a7dea167SDimitry Andric } 154a7dea167SDimitry Andric 155e8d8bef9SDimitry Andric SmallString<128> Buffer; 156a7dea167SDimitry Andric llvm::raw_svector_ostream Out(Buffer); 157a7dea167SDimitry Andric F->describe(Out); 158*0fca6ea1SDimitry Andric if (!Buffer.empty()) 1595f757f3fSDimitry Andric addDiag(CallRange.getBegin(), diag::note_constexpr_call_here) 1605f757f3fSDimitry Andric << Out.str() << CallRange; 161a7dea167SDimitry Andric } 162a7dea167SDimitry Andric } 163