https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/196334
>From a5f128638d1b14225ee7b52b18d9e0150c6f88eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Wed, 6 May 2026 16:42:26 +0200 Subject: [PATCH] cu --- clang/lib/AST/ByteCode/Compiler.cpp | 16 +++-- clang/lib/AST/ByteCode/Compiler.h | 2 +- clang/lib/AST/ByteCode/Interp.cpp | 22 +++++-- clang/lib/AST/ByteCode/Interp.h | 51 +++++++++++++-- clang/lib/AST/ByteCode/Opcodes.td | 1 + clang/lib/AST/ByteCode/Pointer.cpp | 17 ++--- clang/lib/AST/ByteCode/Program.cpp | 16 +++-- clang/lib/AST/ByteCode/Program.h | 6 +- .../SemaCXX/constant-expression-p2280r4.cpp | 64 ++++++++----------- 9 files changed, 122 insertions(+), 73 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 11c5478a5c748..f9d13b42ac0bd 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -5270,7 +5270,8 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD, return checkDecl(); // The previous attempt at initialization might've been unsuccessful, // so let's try this one. - } else if ((GlobalIndex = P.createGlobal(VD, Init))) { + } else if ((GlobalIndex = + P.createGlobal(VD, Init, VariablesAreConstexprUnknown))) { } else { return false; } @@ -7617,7 +7618,9 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) { if (IsReference) { if (!Ctx.getLangOpts().CPlusPlus11) return this->emitGetGlobal(classifyPrim(E), *GlobalIndex, E); - return this->emitGetGlobalUnchecked(classifyPrim(E), *GlobalIndex, E); + if (!Ctx.getLangOpts().CPlusPlus23) + return this->emitGetGlobalUnchecked(classifyPrim(E), *GlobalIndex, E); + return this->emitGetRefGlobal(*GlobalIndex, E); } return this->emitGetPtrGlobal(*GlobalIndex, E); @@ -7702,7 +7705,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) { true); return this->visitDeclRef(D, E); } - return revisit(VD); + return revisit(VD, !VD->isConstexpr() && DeclType->isReferenceType()); } // FIXME: The evaluateValue() check here is a little ridiculous, since @@ -7726,7 +7729,8 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) { /*InitializerFailed=*/true, E); } - return this->emitDummyPtr(D, E); + return this->emitDummyPtr( + D, E, Ctx.getLangOpts().CPlusPlus23 && DeclType->isReferenceType()); } template <class Emitter> @@ -8034,9 +8038,9 @@ bool Compiler<Emitter>::emitDestructionPop(const Descriptor *Desc, /// Create a dummy pointer for the given decl (or expr) and /// push a pointer to it on the stack. template <class Emitter> -bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E) { +bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E, bool CU) { assert(!DiscardResult && "Should've been checked before"); - unsigned DummyID = P.getOrCreateDummy(D); + unsigned DummyID = P.getOrCreateDummy(D, CU); if (!this->emitGetPtrGlobal(DummyID, E)) return false; diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index de6ea524897a0..98eba30415632 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -417,7 +417,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, const BinaryOperator *E); bool emitRecordDestructionPop(const Record *R, SourceInfo Loc); bool emitDestructionPop(const Descriptor *Desc, SourceInfo Loc); - bool emitDummyPtr(const DeclTy &D, const Expr *E); + bool emitDummyPtr(const DeclTy &D, const Expr *E, bool CU = false); bool emitFloat(const APFloat &F, const Expr *E); unsigned collectBaseOffset(const QualType BaseType, const QualType DerivedType); diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 30b54f5dab236..ad840dc4fd404 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -397,7 +397,8 @@ bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; const auto *VD = Ptr.getDeclDesc()->asValueDecl(); - diagnoseNonConstVariable(S, OpPC, VD); + if (!Ptr.isConstexprUnknown() || !S.checkingPotentialConstantExpression()) + diagnoseNonConstVariable(S, OpPC, VD); return false; } @@ -1539,9 +1540,7 @@ bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK, return true; } - if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived)) - return false; - if (!CheckDowncast(S, OpPC, Ptr, Off)) + if (isConstexprUnknown(Ptr)) return false; if (!Ptr.getFieldDesc()->isRecord()) { @@ -1549,6 +1548,11 @@ bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK, return true; } + if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived)) + return false; + if (!CheckDowncast(S, OpPC, Ptr, Off)) + return false; + const Record *TargetRecord = Ptr.atFieldSub(Off).getRecord(); assert(TargetRecord); @@ -2399,6 +2403,16 @@ bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) { if (!P.isBlockPointer()) return false; + if (P.isConstexprUnknown()) { + QualType DynamicType = P.getType(); + const Expr *E = S.Current->getExpr(OpPC); + APValue V = P.toAPValue(S.getASTContext()); + QualType TT = S.getASTContext().getLValueReferenceType(DynamicType); + S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type) + << AK_TypeId << V.getAsString(S.getASTContext(), TT); + return false; + } + // Pick the most-derived type. CanQualType T = P.getDeclPtr().getType()->getCanonicalTypeUnqualified(); // ... unless we're currently constructing this object. diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 4a550fdd63bfb..d9cd7489b30f6 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1277,6 +1277,15 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { } } + // p == nullptr or nullptr == p. + if (RHS.isZero() || LHS.isZero()) { + S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered))); + return true; + } + + assert(!LHS.isZero()); + assert(!RHS.isZero()); + if (!S.inConstantContext()) { if (isConstexprUnknown(LHS) || isConstexprUnknown(RHS)) return false; @@ -1309,27 +1318,26 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { } // Otherwise we need to do a bunch of extra checks before returning Unordered. - if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() && - RHS.isBlockPointer() && RHS.getOffset() == 0) { + if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && RHS.isBlockPointer() && + RHS.getOffset() == 0) { const SourceInfo &Loc = S.Current->getSource(OpPC); S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end) << LHS.toDiagnosticString(S.getASTContext()); return false; } - if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() && - LHS.isBlockPointer() && LHS.getOffset() == 0) { + if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && LHS.isBlockPointer() && + LHS.getOffset() == 0) { const SourceInfo &Loc = S.Current->getSource(OpPC); S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end) << RHS.toDiagnosticString(S.getASTContext()); return false; } - bool BothNonNull = !LHS.isZero() && !RHS.isZero(); // Reject comparisons to literals. for (const auto &P : {LHS, RHS}) { if (P.isZero()) continue; - if (BothNonNull && P.pointsToLiteral()) { + if (P.pointsToLiteral()) { const Expr *E = P.getDeclDesc()->asExpr(); if (isa<StringLiteral>(E)) { const SourceInfo &Loc = S.Current->getSource(OpPC); @@ -1343,7 +1351,7 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { << P.toDiagnosticString(S.getASTContext()); return false; } - } else if (BothNonNull && P.isIntegralPointer()) { + } else if (P.isIntegralPointer()) { const SourceInfo &Loc = S.Current->getSource(OpPC); S.FFDiag(Loc, diag::note_constexpr_pointer_constant_comparison) << LHS.toDiagnosticString(S.getASTContext()) @@ -1360,6 +1368,15 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { return false; } + if (LHS.isConstexprUnknown() || RHS.isConstexprUnknown()) { + if (!S.checkingPotentialConstantExpression()) + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_pointer_comparison_unspecified) + << LHS.toDiagnosticString(S.getASTContext()) + << RHS.toDiagnosticString(S.getASTContext()); + return false; + } + S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered))); return true; } @@ -1977,6 +1994,22 @@ inline bool GetRefLocal(InterpState &S, CodePtr OpPC, uint32_t I) { return handleReference(S, OpPC, LocalBlock); } +inline bool GetRefGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { + Block *B = S.P.getGlobal(I); + + if (isConstexprUnknown(B)) { + S.Stk.push<Pointer>(B); + return true; + } + + const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>(); + if (Desc.InitState != GlobalInitState::Initialized) + return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read); + + S.Stk.push<Pointer>(B->deref<Pointer>()); + return true; +} + inline bool CheckRefInit(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); return CheckRange(S, OpPC, Ptr, AK_Read); @@ -2076,6 +2109,10 @@ inline bool CheckNull(InterpState &S, CodePtr OpPC) { inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl, const Pointer &Ptr) { + if (!Ptr.isBlockPointer()) + return false; + if (!Ptr.getFieldDesc()->isRecord()) + return false; Pointer Base = Ptr.stripBaseCasts(); const Record::Base *VirtBase = Base.getRecord()->getVirtualBase(Decl); S.Stk.push<Pointer>(Base.atField(VirtBase->Offset)); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 3fb25a5fa0884..9123719ec7f8e 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -325,6 +325,7 @@ def GetPtrLocal : OffsetOpcode { def GetRefLocal : OffsetOpcode { bit HasCustomEval = 1; } +def GetRefGlobal : OffsetOpcode; def CheckRefInit : Opcode {} diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 851e9d5742ccb..51ef1401c3603 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -230,11 +230,6 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)); }; - bool UsePath = true; - if (const ValueDecl *VD = getDeclDesc()->asValueDecl(); - VD && VD->getType()->isReferenceType()) - UsePath = false; - // Build the path into the object. bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray(); Pointer Ptr = *this; @@ -318,10 +313,9 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { // Just invert the order of the elements. std::reverse(Path.begin(), Path.end()); - if (UsePath) - return APValue(Base, Offset, Path, OnePastEnd); - - return APValue(Base, Offset, APValue::NoLValuePath()); + auto Result = APValue(Base, Offset, Path, OnePastEnd); + Result.setConstexprUnknown(isConstexprUnknown()); + return Result; } void Pointer::print(llvm::raw_ostream &OS) const { @@ -437,7 +431,10 @@ std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const { if (isIntegralPointer()) return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str(); - return toAPValue(Ctx).getAsString(Ctx, getType()); + QualType Ty = getType(); + if (Ty->isLValueReferenceType()) + Ty = Ty->getPointeeType(); + return toAPValue(Ctx).getAsString(Ctx, Ty); } bool Pointer::isInitialized() const { diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index d49903e36d34a..6711c61a307fa 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -122,7 +122,7 @@ UnsignedOrNone Program::getOrCreateGlobal(const ValueDecl *VD, return std::nullopt; } -unsigned Program::getOrCreateDummy(const DeclTy &D) { +unsigned Program::getOrCreateDummy(const DeclTy &D, bool IsConstexprUnknown) { assert(D); // Dedup blocks since they are immutable and pointers cannot be compared. if (auto It = DummyVariables.find(D.getOpaqueValue()); @@ -152,6 +152,8 @@ unsigned Program::getOrCreateDummy(const DeclTy &D) { if (!Desc) Desc = allocateDescriptor(D); + Desc->IsConstexprUnknown = IsConstexprUnknown; + assert(Desc); // Allocate a block for storage. @@ -168,7 +170,8 @@ unsigned Program::getOrCreateDummy(const DeclTy &D) { return I; } -UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init) { +UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init, + bool IsConstexprUnknown) { bool IsStatic, IsExtern; bool IsWeak = VD->isWeak(); if (const auto *Var = dyn_cast<VarDecl>(VD)) { @@ -185,8 +188,8 @@ UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init) { // Register all previous declarations as well. For extern blocks, just replace // the index with the new variable. - UnsignedOrNone Idx = - createGlobal(VD, VD->getType(), IsStatic, IsExtern, IsWeak, Init); + UnsignedOrNone Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, + IsWeak, IsConstexprUnknown, Init); if (!Idx) return std::nullopt; @@ -237,7 +240,8 @@ UnsignedOrNone Program::createGlobal(const Expr *E, QualType ExprType) { if (auto Idx = getGlobal(E)) return Idx; if (auto Idx = createGlobal(E, ExprType, /*IsStatic=*/true, - /*IsExtern=*/false, /*IsWeak=*/false)) { + /*IsExtern=*/false, /*IsWeak=*/false, + /*IsConstexprUnknown=*/false)) { GlobalIndices[E] = *Idx; return *Idx; } @@ -246,6 +250,7 @@ UnsignedOrNone Program::createGlobal(const Expr *E, QualType ExprType) { UnsignedOrNone Program::createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, bool IsExtern, bool IsWeak, + bool IsConstexprUnknown, const Expr *Init) { // Create a descriptor for the global. Descriptor *Desc; @@ -261,6 +266,7 @@ UnsignedOrNone Program::createGlobal(const DeclTy &D, QualType Ty, if (!Desc) return std::nullopt; + Desc->IsConstexprUnknown = IsConstexprUnknown; // Allocate a block for storage. unsigned I = Globals.size(); diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index e3ec0c07736a3..c98c5c0e51a24 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -88,10 +88,11 @@ class Program final { const Expr *Init = nullptr); /// Returns or creates a dummy value for unknown declarations. - unsigned getOrCreateDummy(const DeclTy &D); + unsigned getOrCreateDummy(const DeclTy &D, bool IsConstexprUnknown = false); /// Creates a global and returns its index. - UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init); + UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init, + bool IsConstexprUnknown = false); /// Creates a global from a lifetime-extended temporary. UnsignedOrNone createGlobal(const Expr *E, QualType ExprType); @@ -169,6 +170,7 @@ class Program final { UnsignedOrNone createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, bool IsExtern, bool IsWeak, + bool IsConstexprUnknown, const Expr *Init = nullptr); /// Reference to the VM context. diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp index f4a8fbcbbe427..319c6c842e8f8 100644 --- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp +++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp @@ -50,12 +50,11 @@ void splash(Swim& swam) { // expected-note {{declared here}} } extern Swim dc; -extern Swim& trident; // interpreter-note {{declared here}} +extern Swim& trident; constexpr auto& sandeno = typeid(dc); // ok: can only be typeid(Swim) constexpr auto& gallagher = typeid(trident); // expected-error {{constexpr variable 'gallagher' must be initialized by a constant expression}} \ - // nointerpreter-note {{typeid applied to object 'trident' whose dynamic type is not constant}} \ - // interpreter-note {{initializer of 'trident' is unknown}} + // expected-note {{typeid applied to object 'trident' whose dynamic type is not constant}} namespace explicitThis { struct C { @@ -266,9 +265,9 @@ namespace param_reference { namespace dropped_note { extern int &x; // expected-note {{declared here}} - constexpr int f() { return x; } // nointerpreter-note {{read of non-constexpr variable 'x'}} \ - // interpreter-note {{initializer of 'x' is unknown}} - constexpr int y = f(); // expected-error {{constexpr variable 'y' must be initialized by a constant expression}} expected-note {{in call to 'f()'}} + constexpr int f() { return x; } // expected-note {{read of non-constexpr variable 'x'}} + constexpr int y = f(); // expected-error {{constexpr variable 'y' must be initialized by a constant expression}} \ + // expected-note {{in call to 'f()'}} } namespace dynamic { @@ -289,10 +288,10 @@ namespace unsized_array { constexpr int t1 = a - a; constexpr int t2 = a - b; // expected-error {{constexpr variable 't2' must be initialized by a constant expression}} \ // nointerpreter-note {{arithmetic involving unrelated objects '&a[0]' and '&b[0]' has unspecified value}} \ - // interpreter-note {{arithmetic involving unrelated objects 'a' and 'b' has unspecified value}} + // interpreter-note {{arithmetic involving unrelated objects '&a' and '&b' has unspecified value}} constexpr int t3 = a - &c[2]; // expected-error {{constexpr variable 't3' must be initialized by a constant expression}} \ - // nointerpreter-note {{arithmetic involving unrelated objects '&a[0]' and '&c[2]' has unspecified value}} \ - // interpreter-note {{arithmetic involving unrelated objects 'a' and '*((char*)&c + 8)' has unspecified value}} + // nointerpreter-note {{arithmetic involving unrelated objects '&a[0]' and '&c[2]' has unspecified value}} \ + // interpreter-note {{arithmetic involving unrelated objects '&a' and '&c[2]' has unspecified value}} } } @@ -300,24 +299,19 @@ namespace casting { struct A {}; struct B : A {}; struct C : A {}; - extern A &a; // interpreter-note {{declared here}} - extern B &b; // expected-note {{declared here}} interpreter-note 2 {{declared here}} + extern A &a; + extern B &b; // nointerpreter-note {{declared here}} constexpr B &t1 = (B&)a; // expected-error {{must be initialized by a constant expression}} \ - // nointerpreter-note {{cannot cast object of dynamic type 'A' to type 'B'}} \ - // interpreter-note {{initializer of 'a' is unknown}} + // nointerpreter-note {{cannot cast object of dynamic type 'A' to type 'B'}} constexpr B &t2 = (B&)(A&)b; // expected-error {{must be initialized by a constant expression}} \ - // nointerpreter-note {{initializer of 'b' is not a constant expression}} \ - // interpreter-note {{initializer of 'b' is unknown}} - // FIXME: interpreter incorrectly rejects. - constexpr bool t3 = &b + 1 == &(B&)(A&)b; // interpreter-error {{must be initialized by a constant expression}} \ - // interpreter-note {{initializer of 'b' is unknown}} + // nointerpreter-note {{initializer of 'b' is not a constant expression}} + constexpr bool t3 = &b + 1 == &(B&)(A&)b; // interpreter-error {{constexpr variable 't3' must be initialized by a constant expression}} constexpr C &t4 = (C&)(A&)b; // expected-error {{must be initialized by a constant expression}} \ - // nointerpreter-note {{cannot cast object of dynamic type 'B' to type 'C'}} \ - // interpreter-note {{initializer of 'b' is unknown}} + // nointerpreter-note {{cannot cast object of dynamic type 'B' to type 'C'}} } namespace pointer_comparisons { - extern int &extern_n; // interpreter-note 4 {{declared here}} + extern int &extern_n; extern int &extern_n2; constexpr int f1(bool b, int& n) { if (b) { @@ -325,30 +319,25 @@ namespace pointer_comparisons { } return f1(true, n); } - // FIXME: interpreter incorrectly rejects; both sides are the same constexpr-unknown value. - static_assert(f1(false, extern_n)); // interpreter-error {{static assertion expression is not an integral constant expression}} \ - // interpreter-note {{initializer of 'extern_n' is unknown}} + static_assert(f1(false, extern_n)); static_assert(&extern_n != &extern_n2); // expected-error {{static assertion expression is not an integral constant expression}} \ - // nointerpreter-note {{comparison between pointers to unrelated objects '&extern_n' and '&extern_n2' has unspecified value}} \ - // interpreter-note {{initializer of 'extern_n' is unknown}} + // expected-note {{comparison between pointers to unrelated objects '&extern_n' and '&extern_n2' has unspecified value}} void f2(const int &n) { - constexpr int x = &x == &n; // nointerpreter-error {{must be initialized by a constant expression}} \ - // nointerpreter-note {{comparison between pointers to unrelated objects '&x' and '&n' has unspecified value}} + constexpr int x = &x == &n; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{comparison between pointers to unrelated objects '&x' and '&n' has unspecified value}} // Distinct variables are not equal, even if they're local variables. constexpr int y = &x == &y; static_assert(!y); } constexpr int f3() { int x; - return &x == &extern_n; // nointerpreter-note {{comparison between pointers to unrelated objects '&x' and '&extern_n' has unspecified value}} \ - // interpreter-note {{initializer of 'extern_n' is unknown}} + return &x == &extern_n; // expected-note {{comparison between pointers to unrelated objects '&x' and '&extern_n' has unspecified value}} } static_assert(!f3()); // expected-error {{static assertion expression is not an integral constant expression}} \ // expected-note {{in call to 'f3()'}} constexpr int f4() { int *p = new int; - bool b = p == &extern_n; // nointerpreter-note {{comparison between pointers to unrelated objects '&{*new int#0}' and '&extern_n' has unspecified value}} \ - // interpreter-note {{initializer of 'extern_n' is unknown}} + bool b = p == &extern_n; // expected-note {{comparison between pointers to unrelated objects '&{*new int#0}' and '&extern_n' has unspecified value}} delete p; return b; } @@ -383,17 +372,16 @@ namespace enable_if_2 { } namespace GH150015 { - extern int (& c)[8]; // interpreter-note {{declared here}} + extern int (& c)[8]; constexpr int x = c <= c+8; // interpreter-error {{constexpr variable 'x' must be initialized by a constant expression}} \ - // interpreter-note {{initializer of 'c' is unknown}} + // interpreter-note {{cannot refer to element 8 of non-array object in a constant expression}} struct X {}; struct Y {}; struct Z : X, Y {}; - extern Z &z; // interpreter-note{{declared here}} - constexpr int bases = (void*)(X*)&z <= (Y*)&z; // expected-error {{constexpr variable 'bases' must be initialized by a constant expression}} \ - // nointerpreter-note {{comparison of addresses of subobjects of different base classes has unspecified value}} \ - // interpreter-note {{initializer of 'z' is unknown}} + extern Z &z; + constexpr int bases = (void*)(X*)&z <= (Y*)&z; // nointerpreter-error {{constexpr variable 'bases' must be initialized by a constant expression}} \ + // nointerpreter-note {{comparison of addresses of subobjects of different base classes has unspecified value}} } namespace InvalidConstexprFn { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
