diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp | 571 |
1 files changed, 459 insertions, 112 deletions
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp index 807b860f3565..0f9eedc3f38e 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp @@ -7,10 +7,9 @@ //===----------------------------------------------------------------------===// #include "Interp.h" -#include <limits> -#include <vector> #include "Function.h" #include "InterpFrame.h" +#include "InterpShared.h" #include "InterpStack.h" #include "Opcode.h" #include "PrimType.h" @@ -19,9 +18,15 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "llvm/ADT/APSInt.h" +#include "llvm/ADT/StringExtras.h" +#include <limits> +#include <vector> + +using namespace clang; using namespace clang; using namespace clang::interp; @@ -53,22 +58,67 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { return true; } +static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC, + const ValueDecl *VD) { + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD; + S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange(); +} + +static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, + const ValueDecl *VD); +static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC, + const ValueDecl *D) { + const SourceInfo &E = S.Current->getSource(OpPC); + + if (isa<ParmVarDecl>(D)) { + if (S.getLangOpts().CPlusPlus11) { + S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D; + S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange(); + } else { + S.FFDiag(E); + } + return false; + } + + if (!D->getType().isConstQualified()) + diagnoseNonConstVariable(S, OpPC, D); + else if (const auto *VD = dyn_cast<VarDecl>(D); + VD && !VD->getAnyInitializer()) + diagnoseMissingInitializer(S, OpPC, VD); + + return false; +} + static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC, const ValueDecl *VD) { if (!S.getLangOpts().CPlusPlus) return; const SourceInfo &Loc = S.Current->getSource(OpPC); + if (const auto *VarD = dyn_cast<VarDecl>(VD); + VarD && VarD->getType().isConstQualified() && + !VarD->getAnyInitializer()) { + diagnoseMissingInitializer(S, OpPC, VD); + return; + } - if (VD->getType()->isIntegralOrEnumerationType()) + // Rather random, but this is to match the diagnostic output of the current + // interpreter. + if (isa<ObjCIvarDecl>(VD)) + return; + + if (VD->getType()->isIntegralOrEnumerationType()) { S.FFDiag(Loc, diag::note_constexpr_ltor_non_const_int, 1) << VD; - else - S.FFDiag(Loc, - S.getLangOpts().CPlusPlus11 - ? diag::note_constexpr_ltor_non_constexpr - : diag::note_constexpr_ltor_non_integral, - 1) - << VD << VD->getType(); + S.Note(VD->getLocation(), diag::note_declared_at); + return; + } + + S.FFDiag(Loc, + S.getLangOpts().CPlusPlus11 ? diag::note_constexpr_ltor_non_constexpr + : diag::note_constexpr_ltor_non_integral, + 1) + << VD << VD->getType(); S.Note(VD->getLocation(), diag::note_declared_at); } @@ -141,7 +191,7 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { namespace clang { namespace interp { static void popArg(InterpState &S, const Expr *Arg) { - PrimType Ty = S.getContext().classify(Arg->getType()).value_or(PT_Ptr); + PrimType Ty = S.getContext().classify(Arg).value_or(PT_Ptr); TYPE_SWITCH(Ty, S.Stk.discard<T>()); } @@ -169,16 +219,27 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) { // CallExpr we're look for is at the return PC of the current function, i.e. // in the caller. // This code path should be executed very rarely. - const auto *CE = - cast<CallExpr>(S.Current->Caller->getExpr(S.Current->getRetPC())); - unsigned FixedParams = CurFunc->getNumParams(); - int32_t ArgsToPop = CE->getNumArgs() - FixedParams; - assert(ArgsToPop >= 0); - for (int32_t I = ArgsToPop - 1; I >= 0; --I) { - const Expr *A = CE->getArg(FixedParams + I); + unsigned NumVarArgs; + const Expr *const *Args = nullptr; + unsigned NumArgs = 0; + const Expr *CallSite = S.Current->Caller->getExpr(S.Current->getRetPC()); + if (const auto *CE = dyn_cast<CallExpr>(CallSite)) { + Args = CE->getArgs(); + NumArgs = CE->getNumArgs(); + } else if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite)) { + Args = CE->getArgs(); + NumArgs = CE->getNumArgs(); + } else + assert(false && "Can't get arguments from that expression type"); + + assert(NumArgs >= CurFunc->getNumWrittenParams()); + NumVarArgs = NumArgs - CurFunc->getNumWrittenParams(); + for (unsigned I = 0; I != NumVarArgs; ++I) { + const Expr *A = Args[NumArgs - 1 - I]; popArg(S, A); } } + // And in any case, remove the fixed parameters (the non-variadic ones) // at the end. S.Current->popArgs(); @@ -188,6 +249,10 @@ bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!Ptr.isExtern()) return true; + if (Ptr.isInitialized() || + (Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl)) + return true; + if (!S.checkingPotentialConstantExpression() && S.getLangOpts().CPlusPlus) { const auto *VD = Ptr.getDeclDesc()->asValueDecl(); diagnoseNonConstVariable(S, OpPC, VD); @@ -240,10 +305,12 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) { if (VD->isConstexpr()) return true; + QualType T = VD->getType(); if (S.getLangOpts().CPlusPlus && !S.getLangOpts().CPlusPlus11) - return false; + return (T->isSignedIntegerOrEnumerationType() || + T->isUnsignedIntegerOrEnumerationType()) && + T.isConstQualified(); - QualType T = VD->getType(); if (T.isConstQualified()) return true; @@ -256,31 +323,29 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) { return false; }; - if (const auto *D = Desc->asValueDecl()) { - if (const auto *VD = dyn_cast<VarDecl>(D); - VD && VD->hasGlobalStorage() && !IsConstType(VD)) { - diagnoseNonConstVariable(S, OpPC, VD); - return S.inConstantContext(); - } + if (const auto *D = Desc->asVarDecl(); + D && D->hasGlobalStorage() && D != S.EvaluatingDecl && !IsConstType(D)) { + diagnoseNonConstVariable(S, OpPC, D); + return S.inConstantContext(); } return true; } static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (Ptr.isIntegralPointer()) + return true; return CheckConstant(S, OpPC, Ptr.getDeclDesc()); } -bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - return !Ptr.isZero() && !Ptr.isDummy(); -} - bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, CheckSubobjectKind CSK) { if (!Ptr.isZero()) return true; const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK; + S.FFDiag(Loc, diag::note_constexpr_null_subobject) + << CSK << S.Current->getRange(OpPC); + return false; } @@ -289,7 +354,8 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, if (!Ptr.isOnePastEnd()) return true; const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK; + S.FFDiag(Loc, diag::note_constexpr_access_past_end) + << AK << S.Current->getRange(OpPC); return false; } @@ -298,7 +364,8 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, if (!Ptr.isElementPastEnd()) return true; const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK; + S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) + << CSK << S.Current->getRange(OpPC); return false; } @@ -308,23 +375,53 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return true; const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK; + S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) + << CSK << S.Current->getRange(OpPC); + return false; +} + +bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + uint32_t Offset) { + uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize(); + uint32_t PtrOffset = Ptr.getByteOffset(); + + // We subtract Offset from PtrOffset. The result must be at least + // MinOffset. + if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset) + return true; + + const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC)); + QualType TargetQT = E->getType()->getPointeeType(); + QualType MostDerivedQT = Ptr.getDeclPtr().getType(); + + S.CCEDiag(E, diag::note_constexpr_invalid_downcast) + << MostDerivedQT << TargetQT; + return false; } bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isConst()) + if (!Ptr.isConst() || Ptr.isMutable()) return true; // The This pointer is writable in constructors and destructors, // even if isConst() returns true. - if (const Function *Func = S.Current->getFunction(); - Func && (Func->isConstructor() || Func->isDestructor()) && - Ptr.block() == S.Current->getThis().block()) { - return true; + // TODO(perf): We could be hitting this code path quite a lot in complex + // constructors. Is there a better way to do this? + if (S.Current->getFunction()) { + for (const InterpFrame *Frame = S.Current; Frame; Frame = Frame->Caller) { + if (const Function *Func = Frame->getFunction(); + Func && (Func->isConstructor() || Func->isDestructor()) && + Ptr.block() == Frame->getThis().block()) { + return true; + } + } } + if (!Ptr.isBlockPointer()) + return false; + const QualType Ty = Ptr.getType(); const SourceInfo &Loc = S.Current->getSource(OpPC); S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty; @@ -333,9 +430,14 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isMutable()) { + if (!Ptr.isMutable()) + return true; + + // In C++14 onwards, it is permitted to read a mutable member whose + // lifetime began within the evaluation. + if (S.getLangOpts().CPlusPlus14 && + Ptr.block()->getEvalID() == S.Ctx.getEvalID()) return true; - } const SourceInfo &Loc = S.Current->getSource(OpPC); const FieldDecl *Field = Ptr.getField(); @@ -344,11 +446,46 @@ bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; } +bool CheckVolatile(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + assert(Ptr.isLive()); + + // FIXME: This check here might be kinda expensive. Maybe it would be better + // to have another field in InlineDescriptor for this? + if (!Ptr.isBlockPointer()) + return true; + + QualType PtrType = Ptr.getType(); + if (!PtrType.isVolatileQualified()) + return true; + + const SourceInfo &Loc = S.Current->getSource(OpPC); + if (S.getLangOpts().CPlusPlus) + S.FFDiag(Loc, diag::note_constexpr_access_volatile_type) << AK << PtrType; + else + S.FFDiag(Loc); + return false; +} + bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { + assert(Ptr.isLive()); + if (Ptr.isInitialized()) return true; + if (const auto *VD = Ptr.getDeclDesc()->asVarDecl(); + VD && VD->hasGlobalStorage()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + if (VD->getAnyInitializer()) { + S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD; + S.Note(VD->getLocation(), diag::note_declared_at); + } else { + diagnoseMissingInitializer(S, OpPC, VD); + } + return false; + } + if (!S.checkingPotentialConstantExpression()) { S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit) << AK << /*uninitialized=*/true << S.Current->getRange(OpPC); @@ -356,32 +493,54 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } -bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!CheckLive(S, OpPC, Ptr, AK_Read)) +bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (Ptr.isInitialized()) + return true; + + assert(S.getLangOpts().CPlusPlus); + const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl()); + if ((!VD->hasConstantInitialization() && + VD->mightBeUsableInConstantExpressions(S.getCtx())) || + (S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 && + !VD->hasICEInitializer(S.getCtx()))) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD; + S.Note(VD->getLocation(), diag::note_declared_at); + } + return false; +} + +bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (!CheckLive(S, OpPC, Ptr, AK)) return false; if (!CheckConstant(S, OpPC, Ptr)) return false; - if (!CheckDummy(S, OpPC, Ptr)) + if (!CheckDummy(S, OpPC, Ptr, AK)) return false; if (!CheckExtern(S, OpPC, Ptr)) return false; - if (!CheckRange(S, OpPC, Ptr, AK_Read)) + if (!CheckRange(S, OpPC, Ptr, AK)) return false; - if (!CheckInitialized(S, OpPC, Ptr, AK_Read)) + if (!CheckActive(S, OpPC, Ptr, AK)) return false; - if (!CheckActive(S, OpPC, Ptr, AK_Read)) + if (!CheckInitialized(S, OpPC, Ptr, AK)) return false; - if (!CheckTemporary(S, OpPC, Ptr, AK_Read)) + if (!CheckTemporary(S, OpPC, Ptr, AK)) return false; if (!CheckMutable(S, OpPC, Ptr)) return false; + if (!CheckVolatile(S, OpPC, Ptr, AK)) + return false; return true; } bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!CheckLive(S, OpPC, Ptr, AK_Assign)) return false; + if (!CheckDummy(S, OpPC, Ptr, AK_Assign)) + return false; if (!CheckExtern(S, OpPC, Ptr)) return false; if (!CheckRange(S, OpPC, Ptr, AK_Assign)) @@ -396,10 +555,12 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!CheckLive(S, OpPC, Ptr, AK_MemberCall)) return false; - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, AK_MemberCall)) - return false; + if (!Ptr.isDummy()) { + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckRange(S, OpPC, Ptr, AK_MemberCall)) + return false; + } return true; } @@ -419,45 +580,62 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { return false; } - if (!F->isConstexpr()) { - const SourceLocation &Loc = S.Current->getLocation(OpPC); - if (S.getLangOpts().CPlusPlus11) { - const FunctionDecl *DiagDecl = F->getDecl(); - - // If this function is not constexpr because it is an inherited - // non-constexpr constructor, diagnose that directly. - const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl); - if (CD && CD->isInheritingConstructor()) { - const auto *Inherited = CD->getInheritedConstructor().getConstructor(); - if (!Inherited->isConstexpr()) - DiagDecl = CD = Inherited; - } + if (F->isConstexpr() && F->hasBody() && + (F->getDecl()->isConstexpr() || F->getDecl()->hasAttr<MSConstexprAttr>())) + return true; - // FIXME: If DiagDecl is an implicitly-declared special member function - // or an inheriting constructor, we should be much more explicit about why - // it's not constexpr. - if (CD && CD->isInheritingConstructor()) { - S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1) - << CD->getInheritedConstructor().getConstructor()->getParent(); - S.Note(DiagDecl->getLocation(), diag::note_declared_at); - } else { - // Don't emit anything if the function isn't defined and we're checking - // for a constant expression. It might be defined at the point we're - // actually calling it. - if (!DiagDecl->isDefined() && S.checkingPotentialConstantExpression()) - return false; + // Implicitly constexpr. + if (F->isLambdaStaticInvoker()) + return true; - S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1) - << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; - S.Note(DiagDecl->getLocation(), diag::note_declared_at); - } + const SourceLocation &Loc = S.Current->getLocation(OpPC); + if (S.getLangOpts().CPlusPlus11) { + const FunctionDecl *DiagDecl = F->getDecl(); + + // Invalid decls have been diagnosed before. + if (DiagDecl->isInvalidDecl()) + return false; + + // If this function is not constexpr because it is an inherited + // non-constexpr constructor, diagnose that directly. + const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl); + if (CD && CD->isInheritingConstructor()) { + const auto *Inherited = CD->getInheritedConstructor().getConstructor(); + if (!Inherited->isConstexpr()) + DiagDecl = CD = Inherited; + } + + // FIXME: If DiagDecl is an implicitly-declared special member function + // or an inheriting constructor, we should be much more explicit about why + // it's not constexpr. + if (CD && CD->isInheritingConstructor()) { + S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1) + << CD->getInheritedConstructor().getConstructor()->getParent(); + S.Note(DiagDecl->getLocation(), diag::note_declared_at); } else { - S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); + // Don't emit anything if the function isn't defined and we're checking + // for a constant expression. It might be defined at the point we're + // actually calling it. + bool IsExtern = DiagDecl->getStorageClass() == SC_Extern; + if (!DiagDecl->isDefined() && !IsExtern && DiagDecl->isConstexpr() && + S.checkingPotentialConstantExpression()) + return false; + + // If the declaration is defined, declared 'constexpr' _and_ has a body, + // the below diagnostic doesn't add anything useful. + if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && + DiagDecl->hasBody()) + return false; + + S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1) + << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; + S.Note(DiagDecl->getLocation(), diag::note_declared_at); } - return false; + } else { + S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); } - return true; + return false; } bool CheckCallDepth(InterpState &S, CodePtr OpPC) { @@ -498,17 +676,6 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { return false; } -bool CheckPotentialReinterpretCast(InterpState &S, CodePtr OpPC, - const Pointer &Ptr) { - if (!S.inConstantContext()) - return true; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - return false; -} - bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result, APFloat::opStatus Status) { const SourceInfo &E = S.Current->getSource(OpPC); @@ -556,36 +723,212 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result, return true; } +bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) { + if (S.getLangOpts().CPlusPlus20) + return true; + + const SourceInfo &E = S.Current->getSource(OpPC); + S.CCEDiag(E, diag::note_constexpr_new); + return true; +} + +bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC, bool NewWasArray, + bool DeleteIsArray, const Descriptor *D, + const Expr *NewExpr) { + if (NewWasArray == DeleteIsArray) + return true; + + QualType TypeToDiagnose; + // We need to shuffle things around a bit here to get a better diagnostic, + // because the expression we allocated the block for was of type int*, + // but we want to get the array size right. + if (D->isArray()) { + QualType ElemQT = D->getType()->getPointeeType(); + TypeToDiagnose = S.getCtx().getConstantArrayType( + ElemQT, APInt(64, static_cast<uint64_t>(D->getNumElems()), false), + nullptr, ArraySizeModifier::Normal, 0); + } else + TypeToDiagnose = D->getType()->getPointeeType(); + + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_new_delete_mismatch) + << DeleteIsArray << 0 << TypeToDiagnose; + S.Note(NewExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here) + << NewExpr->getSourceRange(); + return false; +} + +bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source, + const Pointer &Ptr) { + if (Source && isa<CXXNewExpr>(Source)) + return true; + + // Whatever this is, we didn't heap allocate it. + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc) + << Ptr.toDiagnosticString(S.getCtx()); + + if (Ptr.isTemporary()) + S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here); + else + S.Note(Ptr.getDeclLoc(), diag::note_declared_at); + return false; +} + /// We aleady know the given DeclRefExpr is invalid for some reason, /// now figure out why and print appropriate diagnostics. bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) { const ValueDecl *D = DR->getDecl(); - const SourceInfo &E = S.Current->getSource(OpPC); + return diagnoseUnknownDecl(S, OpPC, D); +} - if (isa<ParmVarDecl>(D)) { - if (S.getLangOpts().CPlusPlus11) { - S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D; - S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange(); - } else { - S.FFDiag(E); +bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + AccessKinds AK) { + if (!Ptr.isDummy()) + return true; + + const Descriptor *Desc = Ptr.getDeclDesc(); + const ValueDecl *D = Desc->asValueDecl(); + if (!D) + return false; + + if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement) + return diagnoseUnknownDecl(S, OpPC, D); + + assert(AK == AK_Assign); + if (S.getLangOpts().CPlusPlus11) { + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_constexpr_modify_global); + } + return false; +} + +bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, + const CallExpr *CE, unsigned ArgSize) { + auto Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs()); + auto NonNullArgs = collectNonNullArgs(F->getDecl(), Args); + unsigned Offset = 0; + unsigned Index = 0; + for (const Expr *Arg : Args) { + if (NonNullArgs[Index] && Arg->getType()->isPointerType()) { + const Pointer &ArgPtr = S.Stk.peek<Pointer>(ArgSize - Offset); + if (ArgPtr.isZero()) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); + S.CCEDiag(Loc, diag::note_non_null_attribute_failed); + return false; + } } - } else if (const auto *VD = dyn_cast<VarDecl>(D)) { - if (!VD->getType().isConstQualified()) { - diagnoseNonConstVariable(S, OpPC, VD); - return false; + + Offset += align(primSize(S.Ctx.classify(Arg).value_or(PT_Ptr))); + ++Index; + } + return true; +} + +// FIXME: This is similar to code we already have in Compiler.cpp. +// I think it makes sense to instead add the field and base destruction stuff +// to the destructor Function itself. Then destroying a record would really +// _just_ be calling its destructor. That would also help with the diagnostic +// difference when the destructor or a field/base fails. +static bool runRecordDestructor(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, + const Descriptor *Desc) { + assert(Desc->isRecord()); + const Record *R = Desc->ElemRecord; + assert(R); + + // Fields. + for (const Record::Field &Field : llvm::reverse(R->fields())) { + const Descriptor *D = Field.Desc; + if (D->isRecord()) { + if (!runRecordDestructor(S, OpPC, BasePtr.atField(Field.Offset), D)) + return false; + } else if (D->isCompositeArray()) { + const Descriptor *ElemDesc = Desc->ElemDesc; + assert(ElemDesc->isRecord()); + for (unsigned I = 0; I != Desc->getNumElems(); ++I) { + if (!runRecordDestructor(S, OpPC, BasePtr.atIndex(I).narrow(), + ElemDesc)) + return false; + } } + } + + // Destructor of this record. + if (const CXXDestructorDecl *Dtor = R->getDestructor(); + Dtor && !Dtor->isTrivial()) { + const Function *DtorFunc = S.getContext().getOrCreateFunction(Dtor); + if (!DtorFunc) + return false; - // const, but no initializer. - if (!VD->getAnyInitializer()) { - S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD; - S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange(); + S.Stk.push<Pointer>(BasePtr); + if (!Call(S, OpPC, DtorFunc, 0)) return false; + } + + // Bases. + for (const Record::Base &Base : llvm::reverse(R->bases())) { + if (!runRecordDestructor(S, OpPC, BasePtr.atField(Base.Offset), Base.Desc)) + return false; + } + + return true; +} + +bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { + assert(B); + const Descriptor *Desc = B->getDescriptor(); + + if (Desc->isPrimitive() || Desc->isPrimitiveArray()) + return true; + + assert(Desc->isRecord() || Desc->isCompositeArray()); + + if (Desc->isCompositeArray()) { + const Descriptor *ElemDesc = Desc->ElemDesc; + assert(ElemDesc->isRecord()); + + Pointer RP(const_cast<Block *>(B)); + for (unsigned I = 0; I != Desc->getNumElems(); ++I) { + if (!runRecordDestructor(S, OpPC, RP.atIndex(I).narrow(), ElemDesc)) + return false; } + return true; } - return false; + assert(Desc->isRecord()); + return runRecordDestructor(S, OpPC, Pointer(const_cast<Block *>(B)), Desc); +} + +void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, + const APSInt &Value) { + llvm::APInt Min; + llvm::APInt Max; + + if (S.EvaluatingDecl && !S.EvaluatingDecl->isConstexpr()) + return; + + ED->getValueRange(Max, Min); + --Max; + + if (ED->getNumNegativeBits() && + (Max.slt(Value.getSExtValue()) || Min.sgt(Value.getSExtValue()))) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); + S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range) + << llvm::toString(Value, 10) << Min.getSExtValue() << Max.getSExtValue() + << ED; + } else if (!ED->getNumNegativeBits() && Max.ult(Value.getZExtValue())) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); + S.report(Loc, diag::warn_constexpr_unscoped_enum_out_of_range) + << llvm::toString(Value, 10) << Min.getZExtValue() << Max.getZExtValue() + << ED; + } } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", off) +#endif bool Interpret(InterpState &S, APValue &Result) { // The current stack frame when we started Interpret(). // This is being used by the ops to determine wheter @@ -610,6 +953,10 @@ bool Interpret(InterpState &S, APValue &Result) { } } } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", on) +#endif } // namespace interp } // namespace clang |