161 lines · cpp
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-exception6//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 16using namespace clang;17using namespace clang::interp;18 19State::~State() {}20 21OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,22 unsigned ExtraNotes) {23 return diag(Loc, DiagId, ExtraNotes, false);24}25 26OptionalDiagnostic 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 34OptionalDiagnostic 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 42OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,43 unsigned ExtraNotes) {44 // Don't override a previous diagnostic. Don't bother collecting45 // 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 53OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,54 unsigned ExtraNotes) {55 return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);56}57 58OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,59 unsigned ExtraNotes) {60 return CCEDiag(SI.getLoc(), DiagId, ExtraNotes);61}62 63OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {64 if (!hasActiveDiagnostic())65 return OptionalDiagnostic();66 return OptionalDiagnostic(&addDiag(Loc, DiagId));67}68 69void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {70 if (hasActiveDiagnostic())71 llvm::append_range(*getEvalStatus().Diag, Diags);72}73 74DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {75 return getASTContext().getDiagnostics().Report(Loc, DiagId);76}77 78/// Add a diagnostic to the diagnostics list.79PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {80 PartialDiagnostic PD(DiagId, getASTContext().getDiagAllocator());81 getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));82 return getEvalStatus().Diag->back().second;83}84 85OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,86 unsigned ExtraNotes, bool IsCCEDiag) {87 Expr::EvalStatus &EvalStatus = getEvalStatus();88 if (EvalStatus.Diag) {89 if (hasPriorDiagnostic()) {90 return OptionalDiagnostic();91 }92 93 unsigned CallStackNotes = getCallStackDepth() - 1;94 unsigned Limit =95 getASTContext().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 115void State::addCallStack(unsigned Limit) {116 // Determine which calls to skip, if any.117 unsigned ActiveCalls = getCallStackDepth() - 1;118 unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;119 if (Limit && Limit < ActiveCalls) {120 SkipStart = Limit / 2 + Limit % 2;121 SkipEnd = ActiveCalls - Limit / 2;122 }123 124 // Walk the call stack and add the diagnostics.125 unsigned CallIdx = 0;126 const Frame *Top = getCurrentFrame();127 const Frame *Bottom = getBottomFrame();128 for (const Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) {129 SourceRange CallRange = F->getCallRange();130 assert(CallRange.isValid());131 132 // Skip this call?133 if (CallIdx >= SkipStart && CallIdx < SkipEnd) {134 if (CallIdx == SkipStart) {135 // Note that we're skipping calls.136 addDiag(CallRange.getBegin(), diag::note_constexpr_calls_suppressed)137 << unsigned(ActiveCalls - Limit);138 }139 continue;140 }141 142 // Use a different note for an inheriting constructor, because from the143 // user's perspective it's not really a function at all.144 if (const auto *CD =145 dyn_cast_if_present<CXXConstructorDecl>(F->getCallee());146 CD && CD->isInheritingConstructor()) {147 addDiag(CallRange.getBegin(),148 diag::note_constexpr_inherited_ctor_call_here)149 << CD->getParent();150 continue;151 }152 153 SmallString<128> Buffer;154 llvm::raw_svector_ostream Out(Buffer);155 F->describe(Out);156 if (!Buffer.empty())157 addDiag(CallRange.getBegin(), diag::note_constexpr_call_here)158 << Out.str() << CallRange;159 }160}161