diff options
author | Eli Friedman <efriedma@quicinc.com> | 2025-07-07 23:35:10 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-07 23:35:10 -0700 |
commit | 071765749a70b22fb62f2efc07a3f242ff5b4c52 (patch) | |
tree | a69442d0672ba61d9eee594317a24531aba4885d /clang/lib/AST/ExprConstant.cpp | |
parent | 18991f465425ab054efec8950c1b1a05d2435ce8 (diff) | |
download | llvm-071765749a70b22fb62f2efc07a3f242ff5b4c52.zip llvm-071765749a70b22fb62f2efc07a3f242ff5b4c52.tar.gz llvm-071765749a70b22fb62f2efc07a3f242ff5b4c52.tar.bz2 |
[clang] Improve constexpr-unknown diagnostics. (#146288)
APValue::ConstexprUnknown() constructs a broken LValue that doesn't have
an lvalue path, which confuses later error handling. It turns out we
don't actually use the result of createConstexprUnknownAPValues for
anything, so just stop using it. Just construct the LValue directly when
we need it.
Make findCompleteObject emit errors more aggressively; allowing it to
succeed for constexpr-unknown objects leads to weird states where it
succeeds, but doesn't return a well-formed object.
Delete the check for constexpr-unknown in dynamic_cast handling: it's
not necessary, and breaks with the other changes in this patch.
These changes allow us to produce proper diagnostics when something
fails to be evaluated, instead of just printing a generic top-level
error without any notes.
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 69 |
1 files changed, 27 insertions, 42 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index bf92087..81c778e 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -202,7 +202,9 @@ namespace { assert(!isBaseAnAllocSizeCall(Base) && "Unsized arrays shouldn't appear here"); unsigned MostDerivedLength = 0; - Type = getType(Base); + // The type of Base is a reference type if the base is a constexpr-unknown + // variable. In that case, look through the reference type. + Type = getType(Base).getNonReferenceType(); for (unsigned I = 0, N = Path.size(); I != N; ++I) { if (Type->isArrayType()) { @@ -289,7 +291,7 @@ namespace { : Invalid(false), IsOnePastTheEnd(false), FirstEntryIsAnUnsizedArray(false), MostDerivedIsArrayElement(false), MostDerivedPathLength(0), MostDerivedArraySize(0), - MostDerivedType(T) {} + MostDerivedType(T.isNull() ? QualType() : T.getNonReferenceType()) {} SubobjectDesignator(ASTContext &Ctx, const APValue &V) : Invalid(!V.isLValue() || !V.hasLValuePath()), IsOnePastTheEnd(false), @@ -571,7 +573,6 @@ namespace { typedef std::map<MapKeyTy, APValue> MapTy; /// Temporaries - Temporary lvalues materialized within this stack frame. MapTy Temporaries; - MapTy ConstexprUnknownAPValues; /// CallRange - The source range of the call expression for this call. SourceRange CallRange; @@ -646,9 +647,6 @@ namespace { APValue &createTemporary(const KeyT *Key, QualType T, ScopeKind Scope, LValue &LV); - APValue &createConstexprUnknownAPValues(const VarDecl *Key, - APValue::LValueBase Base); - /// Allocate storage for a parameter of a function call made in this frame. APValue &createParam(CallRef Args, const ParmVarDecl *PVD, LValue &LV); @@ -1756,7 +1754,8 @@ namespace { return; } if (checkSubobject(Info, E, CSK_ArrayToPointer)) { - assert(getType(Base)->isPointerType() || getType(Base)->isArrayType()); + assert(getType(Base).getNonReferenceType()->isPointerType() || + getType(Base).getNonReferenceType()->isArrayType()); Designator.FirstEntryIsAnUnsizedArray = true; Designator.addUnsizedArrayUnchecked(ElemTy); } @@ -1955,15 +1954,6 @@ APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, return createLocal(Base, Key, T, Scope); } -APValue & -CallStackFrame::createConstexprUnknownAPValues(const VarDecl *Key, - APValue::LValueBase Base) { - APValue &Result = ConstexprUnknownAPValues[MapKeyTy(Key, Base.getVersion())]; - Result = APValue(Base, CharUnits::Zero(), APValue::ConstexprUnknown{}); - - return Result; -} - /// Allocate storage for a parameter of a function call made in this frame. APValue &CallStackFrame::createParam(CallRef Args, const ParmVarDecl *PVD, LValue &LV) { @@ -3493,7 +3483,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, APValue::LValueBase Base(VD, Frame ? Frame->Index : 0, Version); auto CheckUninitReference = [&](bool IsLocalVariable) { - if (!Result->hasValue() && VD->getType()->isReferenceType()) { + if (!Result || (!Result->hasValue() && VD->getType()->isReferenceType())) { // C++23 [expr.const]p8 // ... For such an object that is not usable in constant expressions, the // dynamic type of the object is constexpr-unknown. For such a reference @@ -3509,7 +3499,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.FFDiag(E, diag::note_constexpr_use_uninit_reference); return false; } - Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base); + Result = nullptr; } return true; }; @@ -3552,7 +3542,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, // ... its lifetime began within the evaluation of E; if (isa<ParmVarDecl>(VD)) { if (AllowConstexprUnknown) { - Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base); + Result = nullptr; return true; } @@ -3659,12 +3649,8 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, Result = VD->getEvaluatedValue(); - if (!Result) { - if (AllowConstexprUnknown) - Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base); - else - return false; - } + if (!Result && !AllowConstexprUnknown) + return false; return CheckUninitReference(/*IsLocalVariable=*/false); } @@ -3947,11 +3933,6 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, const FieldDecl *LastField = nullptr; const FieldDecl *VolatileField = nullptr; - // C++23 [expr.const]p8 If we have an unknown reference or pointers and it - // does not have a value then bail out. - if (O->allowConstexprUnknown() && !O->hasValue()) - return false; - // Walk the designator's path to find the subobject. for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { // Reading an indeterminate value is undefined, but assigning over one is OK. @@ -4491,6 +4472,15 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!evaluateVarDeclInit(Info, E, VD, Frame, LVal.getLValueVersion(), BaseVal)) return CompleteObject(); + // If evaluateVarDeclInit sees a constexpr-unknown variable, it returns + // a null BaseVal. Any constexpr-unknown variable seen here is an error: + // we can't access a constexpr-unknown object. + if (!BaseVal) { + Info.FFDiag(E, diag::note_constexpr_access_unknown_variable, 1) + << AK << VD; + Info.Note(VD->getLocation(), diag::note_declared_at); + return CompleteObject(); + } } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) { std::optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA); if (!Alloc) { @@ -6057,15 +6047,6 @@ struct CheckDynamicTypeHandler { /// dynamic type. static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This, AccessKinds AK, bool Polymorphic) { - // We are not allowed to invoke a virtual function whose dynamic type - // is constexpr-unknown, so stop early and let this fail later on if we - // attempt to do so. - // C++23 [expr.const]p5.6 - // an invocation of a virtual function ([class.virtual]) for an object whose - // dynamic type is constexpr-unknown; - if (This.allowConstexprUnknown()) - return true; - if (This.Designator.Invalid) return false; @@ -6139,9 +6120,7 @@ static std::optional<DynamicType> ComputeDynamicType(EvalInfo &Info, // meaningful dynamic type. (We consider objects of non-class type to have no // dynamic type.) if (!checkDynamicType(Info, E, This, AK, - (AK == AK_TypeId - ? (E->getType()->isReferenceType() ? true : false) - : true))) + AK != AK_TypeId || This.AllowConstexprUnknown)) return std::nullopt; if (This.Designator.Invalid) @@ -9063,6 +9042,12 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { if (!evaluateVarDeclInit(Info, E, VD, Frame, Version, V)) return false; + if (!V) { + Result.set(VD); + Result.AllowConstexprUnknown = true; + return true; + } + return Success(*V, E); } |