r363295 - C++ DR712 and others: handle non-odr-use resulting from an lvalue-to-rvalue conversion applied to a member access or similar not-quite-trivial lvalue expression.
Nico Weber via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 13 21:02:49 PDT 2019
Hi,
this made clang segfault on some inputs seen in ANGLE. I filed PR42276 with
a reduced repro and reverted this (and the two dependent changes) in
r363352 for now.
Nico
On Thu, Jun 13, 2019 at 2:56 PM Richard Smith via cfe-commits <
cfe-commits at lists.llvm.org> wrote:
> Author: rsmith
> Date: Thu Jun 13 12:00:16 2019
> New Revision: 363295
>
> URL: http://llvm.org/viewvc/llvm-project?rev=363295&view=rev
> Log:
> C++ DR712 and others: handle non-odr-use resulting from an
> lvalue-to-rvalue conversion applied to a member access or similar
> not-quite-trivial lvalue expression.
>
> Summary:
> When a variable is named in a context where we can't directly emit a
> reference to it (because we don't know for sure that it's going to be
> defined, or it's from an enclosing function and not captured, or the
> reference might not "work" for some reason), we emit a copy of the
> variable as a global and use that for the known-to-be-read-only access.
>
> Reviewers: rjmccall
>
> Subscribers: jdoerfert, cfe-commits
>
> Tags: #clang
>
> Differential Revision: https://reviews.llvm.org/D63157
>
> Added:
> cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp
> cfe/trunk/test/CodeGenCXX/no-odr-use.cpp
> Modified:
> cfe/trunk/lib/CodeGen/CGDecl.cpp
> cfe/trunk/lib/CodeGen/CGExpr.cpp
> cfe/trunk/lib/CodeGen/CodeGenModule.h
> cfe/trunk/lib/Sema/SemaExpr.cpp
> cfe/trunk/test/CXX/drs/dr20xx.cpp
> cfe/trunk/test/CXX/drs/dr21xx.cpp
> cfe/trunk/test/CXX/drs/dr23xx.cpp
> cfe/trunk/test/CXX/drs/dr6xx.cpp
> cfe/trunk/test/CXX/drs/dr7xx.cpp
> cfe/trunk/www/cxx_dr_status.html
>
> Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CGDecl.cpp Thu Jun 13 12:00:16 2019
> @@ -1077,17 +1077,16 @@ static llvm::Constant *constWithPadding(
> return constant;
> }
>
> -static Address createUnnamedGlobalFrom(CodeGenModule &CGM, const VarDecl
> &D,
> - CGBuilderTy &Builder,
> - llvm::Constant *Constant,
> - CharUnits Align) {
> +Address CodeGenModule::createUnnamedGlobalFrom(const VarDecl &D,
> + llvm::Constant *Constant,
> + CharUnits Align) {
> auto FunctionName = [&](const DeclContext *DC) -> std::string {
> if (const auto *FD = dyn_cast<FunctionDecl>(DC)) {
> if (const auto *CC = dyn_cast<CXXConstructorDecl>(FD))
> return CC->getNameAsString();
> if (const auto *CD = dyn_cast<CXXDestructorDecl>(FD))
> return CD->getNameAsString();
> - return CGM.getMangledName(FD);
> + return getMangledName(FD);
> } else if (const auto *OM = dyn_cast<ObjCMethodDecl>(DC)) {
> return OM->getNameAsString();
> } else if (isa<BlockDecl>(DC)) {
> @@ -1099,22 +1098,39 @@ static Address createUnnamedGlobalFrom(C
> }
> };
>
> - auto *Ty = Constant->getType();
> - bool isConstant = true;
> - llvm::GlobalVariable *InsertBefore = nullptr;
> - unsigned AS = CGM.getContext().getTargetAddressSpace(
> - CGM.getStringLiteralAddressSpace());
> - llvm::GlobalVariable *GV = new llvm::GlobalVariable(
> - CGM.getModule(), Ty, isConstant, llvm::GlobalValue::PrivateLinkage,
> - Constant,
> - "__const." + FunctionName(D.getParentFunctionOrMethod()) + "." +
> - D.getName(),
> - InsertBefore, llvm::GlobalValue::NotThreadLocal, AS);
> - GV->setAlignment(Align.getQuantity());
> - GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
> -
> - Address SrcPtr = Address(GV, Align);
> - llvm::Type *BP = llvm::PointerType::getInt8PtrTy(CGM.getLLVMContext(),
> AS);
> + // Form a simple per-variable cache of these values in case we find we
> + // want to reuse them.
> + llvm::GlobalVariable *&CacheEntry = InitializerConstants[&D];
> + if (!CacheEntry || CacheEntry->getInitializer() != Constant) {
> + auto *Ty = Constant->getType();
> + bool isConstant = true;
> + llvm::GlobalVariable *InsertBefore = nullptr;
> + unsigned AS =
> +
> getContext().getTargetAddressSpace(getStringLiteralAddressSpace());
> + llvm::GlobalVariable *GV = new llvm::GlobalVariable(
> + getModule(), Ty, isConstant, llvm::GlobalValue::PrivateLinkage,
> + Constant,
> + "__const." + FunctionName(D.getParentFunctionOrMethod()) + "." +
> + D.getName(),
> + InsertBefore, llvm::GlobalValue::NotThreadLocal, AS);
> + GV->setAlignment(Align.getQuantity());
> + GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
> + CacheEntry = GV;
> + } else if (CacheEntry->getAlignment() < Align.getQuantity()) {
> + CacheEntry->setAlignment(Align.getQuantity());
> + }
> +
> + return Address(CacheEntry, Align);
> +}
> +
> +static Address createUnnamedGlobalForMemcpyFrom(CodeGenModule &CGM,
> + const VarDecl &D,
> + CGBuilderTy &Builder,
> + llvm::Constant *Constant,
> + CharUnits Align) {
> + Address SrcPtr = CGM.createUnnamedGlobalFrom(D, Constant, Align);
> + llvm::Type *BP = llvm::PointerType::getInt8PtrTy(CGM.getLLVMContext(),
> +
> SrcPtr.getAddressSpace());
> if (SrcPtr.getType() != BP)
> SrcPtr = Builder.CreateBitCast(SrcPtr, BP);
> return SrcPtr;
> @@ -1197,10 +1213,10 @@ static void emitStoresForConstant(CodeGe
> }
>
> // Copy from a global.
> - Builder.CreateMemCpy(
> - Loc,
> - createUnnamedGlobalFrom(CGM, D, Builder, constant,
> Loc.getAlignment()),
> - SizeVal, isVolatile);
> + Builder.CreateMemCpy(Loc,
> + createUnnamedGlobalForMemcpyFrom(
> + CGM, D, Builder, constant, Loc.getAlignment()),
> + SizeVal, isVolatile);
> }
>
> static void emitStoresForZeroInit(CodeGenModule &CGM, const VarDecl &D,
> @@ -1763,10 +1779,10 @@ void CodeGenFunction::EmitAutoVarInit(co
> llvm::PHINode *Cur = Builder.CreatePHI(Begin.getType(), 2,
> "vla.cur");
> Cur->addIncoming(Begin.getPointer(), OriginBB);
> CharUnits CurAlign =
> Loc.getAlignment().alignmentOfArrayElement(EltSize);
> - Builder.CreateMemCpy(
> - Address(Cur, CurAlign),
> - createUnnamedGlobalFrom(CGM, D, Builder, Constant,
> ConstantAlign),
> - BaseSizeInChars, isVolatile);
> + Builder.CreateMemCpy(Address(Cur, CurAlign),
> + createUnnamedGlobalForMemcpyFrom(
> + CGM, D, Builder, Constant, ConstantAlign),
> + BaseSizeInChars, isVolatile);
> llvm::Value *Next =
> Builder.CreateInBoundsGEP(Int8Ty, Cur, BaseSizeInChars,
> "vla.next");
> llvm::Value *Done = Builder.CreateICmpEQ(Next, End,
> "vla-init.isdone");
>
> Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CGExpr.cpp Thu Jun 13 12:00:16 2019
> @@ -1422,10 +1422,11 @@ static ConstantEmissionKind checkVarType
> }
>
> /// Try to emit a reference to the given value without producing it as
> -/// an l-value. This is actually more than an optimization: we can't
> -/// produce an l-value for variables that we never actually captured
> -/// in a block or lambda, which means const int variables or constexpr
> -/// literals or similar.
> +/// an l-value. This is just an optimization, but it avoids us needing
> +/// to emit global copies of variables if they're named without triggering
> +/// a formal use in a context where we can't emit a direct reference to
> them,
> +/// for instance if a block or lambda or a member of a local class uses a
> +/// const int variable or constexpr variable from an enclosing function.
> CodeGenFunction::ConstantEmission
> CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) {
> ValueDecl *value = refExpr->getDecl();
> @@ -2450,33 +2451,97 @@ static LValue EmitGlobalNamedRegister(co
> return LValue::MakeGlobalReg(Address(Ptr, Alignment), VD->getType());
> }
>
> +/// Determine whether we can emit a reference to \p VD from the current
> +/// context, despite not necessarily having seen an odr-use of the
> variable in
> +/// this context.
> +static bool canEmitSpuriousReferenceToVariable(CodeGenFunction &CGF,
> + const DeclRefExpr *E,
> + const VarDecl *VD,
> + bool IsConstant) {
> + // For a variable declared in an enclosing scope, do not emit a spurious
> + // reference even if we have a capture, as that will emit an unwarranted
> + // reference to our capture state, and will likely generate worse code
> than
> + // emitting a local copy.
> + if (E->refersToEnclosingVariableOrCapture())
> + return false;
> +
> + // For a local declaration declared in this function, we can always
> reference
> + // it even if we don't have an odr-use.
> + if (VD->hasLocalStorage()) {
> + return VD->getDeclContext() ==
> + dyn_cast_or_null<DeclContext>(CGF.CurCodeDecl);
> + }
> +
> + // For a global declaration, we can emit a reference to it if we know
> + // for sure that we are able to emit a definition of it.
> + VD = VD->getDefinition(CGF.getContext());
> + if (!VD)
> + return false;
> +
> + // Don't emit a spurious reference if it might be to a variable that
> only
> + // exists on a different device / target.
> + // FIXME: This is unnecessarily broad. Check whether this would
> actually be a
> + // cross-target reference.
> + if (CGF.getLangOpts().OpenMP || CGF.getLangOpts().CUDA ||
> + CGF.getLangOpts().OpenCL) {
> + return false;
> + }
> +
> + // We can emit a spurious reference only if the linkage implies that
> we'll
> + // be emitting a non-interposable symbol that will be retained until
> link
> + // time.
> + switch (CGF.CGM.getLLVMLinkageVarDefinition(VD, IsConstant)) {
> + case llvm::GlobalValue::ExternalLinkage:
> + case llvm::GlobalValue::LinkOnceODRLinkage:
> + case llvm::GlobalValue::WeakODRLinkage:
> + case llvm::GlobalValue::InternalLinkage:
> + case llvm::GlobalValue::PrivateLinkage:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
> const NamedDecl *ND = E->getDecl();
> QualType T = E->getType();
>
> + assert(E->isNonOdrUse() != NOUR_Unevaluated &&
> + "should not emit an unevaluated operand");
> +
> if (const auto *VD = dyn_cast<VarDecl>(ND)) {
> // Global Named registers access via intrinsics only
> if (VD->getStorageClass() == SC_Register &&
> VD->hasAttr<AsmLabelAttr>() && !VD->isLocalVarDecl())
> return EmitGlobalNamedRegister(VD, CGM);
>
> - // A DeclRefExpr for a reference initialized by a constant expression
> can
> - // appear without being odr-used. Directly emit the constant
> initializer.
> - VD->getAnyInitializer(VD);
> - if (E->isNonOdrUse() == NOUR_Constant &&
> VD->getType()->isReferenceType()) {
> - llvm::Constant *Val =
> - ConstantEmitter(*this).emitAbstract(E->getLocation(),
> - *VD->evaluateValue(),
> - VD->getType());
> - assert(Val && "failed to emit reference constant expression");
> - // FIXME: Eventually we will want to emit vector element references.
> -
> - // Should we be using the alignment of the constant pointer we
> emitted?
> - CharUnits Alignment = getNaturalTypeAlignment(E->getType(),
> - /* BaseInfo= */
> nullptr,
> - /* TBAAInfo= */
> nullptr,
> - /* forPointeeType= */
> true);
> - return MakeAddrLValue(Address(Val, Alignment), T,
> AlignmentSource::Decl);
> + // If this DeclRefExpr does not constitute an odr-use of the variable,
> + // we're not permitted to emit a reference to it in general, and it
> might
> + // not be captured if capture would be necessary for a use. Emit the
> + // constant value directly instead.
> + if (E->isNonOdrUse() == NOUR_Constant &&
> + (VD->getType()->isReferenceType() ||
> + !canEmitSpuriousReferenceToVariable(*this, E, VD, true))) {
> + VD->getAnyInitializer(VD);
> + llvm::Constant *Val = ConstantEmitter(*this).emitAbstract(
> + E->getLocation(), *VD->evaluateValue(), VD->getType());
> + assert(Val && "failed to emit constant expression");
> +
> + Address Addr = Address::invalid();
> + if (!VD->getType()->isReferenceType()) {
> + // Spill the constant value to a global.
> + Addr = CGM.createUnnamedGlobalFrom(*VD, Val,
> + getContext().getDeclAlign(VD));
> + } else {
> + // Should we be using the alignment of the constant pointer we
> emitted?
> + CharUnits Alignment =
> + getNaturalTypeAlignment(E->getType(),
> + /* BaseInfo= */ nullptr,
> + /* TBAAInfo= */ nullptr,
> + /* forPointeeType= */ true);
> + Addr = Address(Val, Alignment);
> + }
> + return MakeAddrLValue(Addr, T, AlignmentSource::Decl);
> }
>
> // FIXME: Handle other kinds of non-odr-use DeclRefExprs.
> @@ -2512,7 +2577,7 @@ LValue CodeGenFunction::EmitDeclRefLValu
> // FIXME: We should be able to assert this for FunctionDecls as well!
> // FIXME: We should be able to assert this for all DeclRefExprs, not
> just
> // those with a valid source location.
> - assert((ND->isUsed(false) || !isa<VarDecl>(ND) ||
> + assert((ND->isUsed(false) || !isa<VarDecl>(ND) || E->isNonOdrUse() ||
> !E->getLocation().isValid()) &&
> "Should not use decl without marking it used!");
>
>
> Modified: cfe/trunk/lib/CodeGen/CodeGenModule.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.h?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CodeGenModule.h (original)
> +++ cfe/trunk/lib/CodeGen/CodeGenModule.h Thu Jun 13 12:00:16 2019
> @@ -362,6 +362,10 @@ private:
> llvm::SmallVector<std::pair<llvm::GlobalValue *, llvm::Constant *>, 8>
> GlobalValReplacements;
>
> + /// Variables for which we've emitted globals containing their constant
> + /// values along with the corresponding globals, for opportunistic
> reuse.
> + llvm::DenseMap<const VarDecl*, llvm::GlobalVariable*>
> InitializerConstants;
> +
> /// Set of global decls for which we already diagnosed mangled name
> conflict.
> /// Required to not issue a warning (on a mangling conflict) multiple
> times
> /// for the same decl.
> @@ -623,6 +627,9 @@ public:
> StaticLocalDeclGuardMap[D] = C;
> }
>
> + Address createUnnamedGlobalFrom(const VarDecl &D, llvm::Constant
> *Constant,
> + CharUnits Align);
> +
> bool lookupRepresentativeDecl(StringRef MangledName,
> GlobalDecl &Result) const;
>
>
> Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu Jun 13 12:00:16 2019
> @@ -15806,6 +15806,32 @@ QualType Sema::getCapturedDeclRefType(Va
> return DeclRefType;
> }
>
> +namespace {
> +// Helper to copy the template arguments from a DeclRefExpr or MemberExpr.
> +// The produced TemplateArgumentListInfo* points to data stored within
> this
> +// object, so should only be used in contexts where the pointer will not
> be
> +// used after the CopiedTemplateArgs object is destroyed.
> +class CopiedTemplateArgs {
> + bool HasArgs;
> + TemplateArgumentListInfo TemplateArgStorage;
> +public:
> + template<typename RefExpr>
> + CopiedTemplateArgs(RefExpr *E) : HasArgs(E->hasExplicitTemplateArgs()) {
> + if (HasArgs)
> + E->copyTemplateArgumentsInto(TemplateArgStorage);
> + }
> + operator TemplateArgumentListInfo*()
> +#ifdef __has_cpp_attribute
> +#if __has_cpp_attribute(clang::lifetimebound)
> + [[clang::lifetimebound]]
> +#endif
> +#endif
> + {
> + return HasArgs ? &TemplateArgStorage : nullptr;
> + }
> +};
> +}
> +
> /// Walk the set of potential results of an expression and mark them all
> as
> /// non-odr-uses if they satisfy the side-conditions of the
> NonOdrUseReason.
> ///
> @@ -15897,16 +15923,11 @@ static ExprResult rebuildPotentialResult
>
> // Rebuild as a non-odr-use DeclRefExpr.
> MarkNotOdrUsed();
> - TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
> - if (DRE->hasExplicitTemplateArgs()) {
> - DRE->copyTemplateArgumentsInto(TemplateArgStorage);
> - TemplateArgs = &TemplateArgStorage;
> - }
> return DeclRefExpr::Create(
> S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(),
> DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(),
> DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(),
> - DRE->getFoundDecl(), TemplateArgs, NOUR);
> + DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR);
> }
>
> case Expr::FunctionParmPackExprClass: {
> @@ -15924,52 +15945,107 @@ static ExprResult rebuildPotentialResult
> break;
> }
>
> - // FIXME: Implement these.
> // -- If e is a subscripting operation with an array operand...
> - // -- If e is a class member access expression [...] naming a
> non-static
> - // data member...
> + case Expr::ArraySubscriptExprClass: {
> + auto *ASE = cast<ArraySubscriptExpr>(E);
> + Expr *OldBase = ASE->getBase()->IgnoreImplicit();
> + if (!OldBase->getType()->isArrayType())
> + break;
> + ExprResult Base = Rebuild(OldBase);
> + if (!Base.isUsable())
> + return Base;
> + Expr *LHS = ASE->getBase() == ASE->getLHS() ? Base.get() :
> ASE->getLHS();
> + Expr *RHS = ASE->getBase() == ASE->getRHS() ? Base.get() :
> ASE->getRHS();
> + SourceLocation LBracketLoc = ASE->getBeginLoc(); // FIXME: Not stored.
> + return S.ActOnArraySubscriptExpr(nullptr, LHS, LBracketLoc, RHS,
> + ASE->getRBracketLoc());
> + }
>
> - // -- If e is a class member access expression naming a static data
> member,
> - // ...
> case Expr::MemberExprClass: {
> auto *ME = cast<MemberExpr>(E);
> + // -- If e is a class member access expression [...] naming a
> non-static
> + // data member...
> + if (isa<FieldDecl>(ME->getMemberDecl())) {
> + ExprResult Base = Rebuild(ME->getBase());
> + if (!Base.isUsable())
> + return Base;
> + return MemberExpr::Create(
> + S.Context, Base.get(), ME->isArrow(), ME->getOperatorLoc(),
> + ME->getQualifierLoc(), ME->getTemplateKeywordLoc(),
> + ME->getMemberDecl(), ME->getFoundDecl(),
> ME->getMemberNameInfo(),
> + CopiedTemplateArgs(ME), ME->getType(), ME->getValueKind(),
> + ME->getObjectKind(), ME->isNonOdrUse());
> + }
> +
> if (ME->getMemberDecl()->isCXXInstanceMember())
> - // FIXME: Recurse to the left-hand side.
> break;
>
> + // -- If e is a class member access expression naming a static data
> member,
> + // ...
> if (ME->isNonOdrUse() ||
> IsPotentialResultOdrUsed(ME->getMemberDecl()))
> break;
>
> // Rebuild as a non-odr-use MemberExpr.
> MarkNotOdrUsed();
> - TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
> - if (ME->hasExplicitTemplateArgs()) {
> - ME->copyTemplateArgumentsInto(TemplateArgStorage);
> - TemplateArgs = &TemplateArgStorage;
> - }
> return MemberExpr::Create(
> S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(),
> ME->getQualifierLoc(), ME->getTemplateKeywordLoc(),
> ME->getMemberDecl(),
> - ME->getFoundDecl(), ME->getMemberNameInfo(), TemplateArgs,
> + ME->getFoundDecl(), ME->getMemberNameInfo(),
> CopiedTemplateArgs(ME),
> ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR);
> return ExprEmpty();
> }
>
> - // FIXME: Implement this.
> - // -- If e is a pointer-to-member expression of the form e1 .* e2 ...
> + case Expr::BinaryOperatorClass: {
> + auto *BO = cast<BinaryOperator>(E);
> + Expr *LHS = BO->getLHS();
> + Expr *RHS = BO->getRHS();
> + // -- If e is a pointer-to-member expression of the form e1 .* e2 ...
> + if (BO->getOpcode() == BO_PtrMemD) {
> + ExprResult Sub = Rebuild(LHS);
> + if (!Sub.isUsable())
> + return Sub;
> + LHS = Sub.get();
> + // -- If e is a comma expression, ...
> + } else if (BO->getOpcode() == BO_Comma) {
> + ExprResult Sub = Rebuild(RHS);
> + if (!Sub.isUsable())
> + return Sub;
> + RHS = Sub.get();
> + } else {
> + break;
> + }
> + return S.BuildBinOp(nullptr, BO->getOperatorLoc(), BO->getOpcode(),
> + LHS, RHS);
> + }
>
> // -- If e has the form (e1)...
> case Expr::ParenExprClass: {
> - auto *PE = dyn_cast<ParenExpr>(E);
> + auto *PE = cast<ParenExpr>(E);
> ExprResult Sub = Rebuild(PE->getSubExpr());
> if (!Sub.isUsable())
> return Sub;
> return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get());
> }
>
> - // FIXME: Implement these.
> // -- If e is a glvalue conditional expression, ...
> - // -- If e is a comma expression, ...
> + // We don't apply this to a binary conditional operator. FIXME: Should
> we?
> + case Expr::ConditionalOperatorClass: {
> + auto *CO = cast<ConditionalOperator>(E);
> + ExprResult LHS = Rebuild(CO->getLHS());
> + if (LHS.isInvalid())
> + return ExprError();
> + ExprResult RHS = Rebuild(CO->getRHS());
> + if (RHS.isInvalid())
> + return ExprError();
> + if (!LHS.isUsable() && !RHS.isUsable())
> + return ExprEmpty();
> + if (!LHS.isUsable())
> + LHS = CO->getLHS();
> + if (!RHS.isUsable())
> + RHS = CO->getRHS();
> + return S.ActOnConditionalOp(CO->getQuestionLoc(), CO->getColonLoc(),
> + CO->getCond(), LHS.get(), RHS.get());
> + }
>
> // [Clang extension]
> // -- If e has the form __extension__ e1...
> @@ -15988,7 +16064,7 @@ static ExprResult rebuildPotentialResult
> // -- If e has the form _Generic(...), the set of potential results
> is the
> // union of the sets of potential results of the associated
> expressions.
> case Expr::GenericSelectionExprClass: {
> - auto *GSE = dyn_cast<GenericSelectionExpr>(E);
> + auto *GSE = cast<GenericSelectionExpr>(E);
>
> SmallVector<Expr *, 4> AssocExprs;
> bool AnyChanged = false;
> @@ -16016,7 +16092,7 @@ static ExprResult rebuildPotentialResult
> // results is the union of the sets of potential results of the
> // second and third subexpressions.
> case Expr::ChooseExprClass: {
> - auto *CE = dyn_cast<ChooseExpr>(E);
> + auto *CE = cast<ChooseExpr>(E);
>
> ExprResult LHS = Rebuild(CE->getLHS());
> if (LHS.isInvalid())
> @@ -16039,13 +16115,38 @@ static ExprResult rebuildPotentialResult
>
> // Step through non-syntactic nodes.
> case Expr::ConstantExprClass: {
> - auto *CE = dyn_cast<ConstantExpr>(E);
> + auto *CE = cast<ConstantExpr>(E);
> ExprResult Sub = Rebuild(CE->getSubExpr());
> if (!Sub.isUsable())
> return Sub;
> return ConstantExpr::Create(S.Context, Sub.get());
> }
>
> + // We could mostly rely on the recursive rebuilding to rebuild implicit
> + // casts, but not at the top level, so rebuild them here.
> + case Expr::ImplicitCastExprClass: {
> + auto *ICE = cast<ImplicitCastExpr>(E);
> + // Only step through the narrow set of cast kinds we expect to
> encounter.
> + // Anything else suggests we've left the region in which potential
> results
> + // can be found.
> + switch (ICE->getCastKind()) {
> + case CK_NoOp:
> + case CK_DerivedToBase:
> + case CK_UncheckedDerivedToBase: {
> + ExprResult Sub = Rebuild(ICE->getSubExpr());
> + if (!Sub.isUsable())
> + return Sub;
> + CXXCastPath Path(ICE->path());
> + return S.ImpCastExprToType(Sub.get(), ICE->getType(),
> ICE->getCastKind(),
> + ICE->getValueKind(), &Path);
> + }
> +
> + default:
> + break;
> + }
> + break;
> + }
> +
> default:
> break;
> }
>
> Added: cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp?rev=363295&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp (added)
> +++ cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp Thu Jun 13 12:00:16 2019
> @@ -0,0 +1,80 @@
> +// RUN: %clang_cc1 -std=c++98 %s -Wno-unused -verify
> +// RUN: %clang_cc1 -std=c++11 %s -Wno-unused -verify
> +// RUN: %clang_cc1 -std=c++2a %s -Wno-unused -verify
> +
> +void use(int);
> +
> +void f() {
> + const int a = 1; // expected-note {{here}}
> +
> +#if __cplusplus >= 201103L
> + constexpr int arr[3] = {1, 2, 3}; // expected-note 2{{here}}
> +
> + struct S { int x; int f() const; };
> + constexpr S s = {0}; // expected-note 3{{here}}
> + constexpr S *ps = nullptr;
> + S *const &psr = ps; // expected-note 2{{here}}
> +#endif
> +
> + struct Inner {
> + void test(int i) {
> + // id-expression
> + use(a);
> +
> +#if __cplusplus >= 201103L
> + // subscripting operation with an array operand
> + use(arr[i]);
> + use(i[arr]);
> + use((+arr)[i]); // expected-error {{reference to local variable}}
> + use(i[+arr]); // expected-error {{reference to local variable}}
> +
> + // class member access naming non-static data member
> + use(s.x);
> + use(s.f()); // expected-error {{reference to local variable}}
> + use((&s)->x); // expected-error {{reference to local variable}}
> + use(ps->x); // ok (lvalue-to-rvalue conversion applied to
> id-expression)
> + use(psr->x); // expected-error {{reference to local variable}}
> +
> + // class member access naming a static data member
> + // FIXME: How to test this?
> +
> + // pointer-to-member expression
> + use(s.*&S::x);
> + use((s.*&S::f)()); // expected-error {{reference to local variable}}
> + use(ps->*&S::x); // ok (lvalue-to-rvalue conversion applied to
> id-expression)
> + use(psr->*&S::x); // expected-error {{reference to local variable}}
> +#endif
> +
> + // parentheses
> + use((a));
> +#if __cplusplus >= 201103L
> + use((s.x));
> +#endif
> +
> + // glvalue conditional expression
> + use(i ? a : a);
> + use(i ? i : a);
> +
> + // comma expression
> + use((i, a));
> + // FIXME: This is not an odr-use because it is a discarded-value
> + // expression applied to an expression whose potential result is
> 'a'.
> + use((a, a)); // expected-error {{reference to local variable}}
> +
> + // (and combinations thereof)
> + use(a ? (i, a) : a);
> +#if __cplusplus >= 201103L
> + use(a ? (i, a) : arr[a ? s.x : arr[a]]);
> +#endif
> + }
> + };
> +}
> +
> +// FIXME: Test that this behaves properly.
> +namespace std_example {
> + struct S { static const int x = 0, y = 0; };
> + const int &f(const int &r);
> + bool b;
> + int n = b ? (1, S::x)
> + : f(S::y);
> +}
>
> Modified: cfe/trunk/test/CXX/drs/dr20xx.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr20xx.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/CXX/drs/dr20xx.cpp (original)
> +++ cfe/trunk/test/CXX/drs/dr20xx.cpp Thu Jun 13 12:00:16 2019
> @@ -4,12 +4,205 @@
> // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify
> -fexceptions -fcxx-exceptions -pedantic-errors
> // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify
> -fexceptions -fcxx-exceptions -pedantic-errors
>
> -// expected-no-diagnostics
> -
> #if __cplusplus < 201103L
> #define static_assert(...) _Static_assert(__VA_ARGS__)
> #endif
>
> +namespace dr2083 { // dr2083: partial
> +#if __cplusplus >= 201103L
> + void non_const_mem_ptr() {
> + struct A {
> + int x;
> + int y;
> + };
> + constexpr A a = {1, 2};
> + struct B {
> + int A::*p;
> + constexpr int g() const {
> + // OK, not an odr-use of 'a'.
> + return a.*p;
> + };
> + };
> + static_assert(B{&A::x}.g() == 1, "");
> + static_assert(B{&A::y}.g() == 2, "");
> + }
> +#endif
> +
> + const int a = 1;
> + int b;
> + // Note, references only get special odr-use / constant initializxer
> + // treatment in C++11 onwards. We continue to apply that even after
> DR2083.
> + void ref_to_non_const() {
> + int c;
> + const int &ra = a; // expected-note 0-1{{here}}
> + int &rb = b; // expected-note 0-1{{here}}
> + int &rc = c; // expected-note {{here}}
> + struct A {
> + int f() {
> + int a = ra;
> + int b = rb;
> +#if __cplusplus < 201103L
> + // expected-error at -3 {{in enclosing function}}
> + // expected-error at -3 {{in enclosing function}}
> +#endif
> + int c = rc; // expected-error {{in enclosing function}}
> + return a + b + c;
> + }
> + };
> + }
> +
> +#if __cplusplus >= 201103L
> + struct NoMut1 { int a, b; };
> + struct NoMut2 { NoMut1 m; };
> + struct NoMut3 : NoMut1 {
> + constexpr NoMut3(int a, int b) : NoMut1{a, b} {}
> + };
> + struct Mut1 {
> + int a;
> + mutable int b;
> + };
> + struct Mut2 { Mut1 m; };
> + struct Mut3 : Mut1 {
> + constexpr Mut3(int a, int b) : Mut1{a, b} {}
> + };
> + void mutable_subobjects() {
> + constexpr NoMut1 nm1 = {1, 2};
> + constexpr NoMut2 nm2 = {1, 2};
> + constexpr NoMut3 nm3 = {1, 2};
> + constexpr Mut1 m1 = {1, 2}; // expected-note {{declared here}}
> + constexpr Mut2 m2 = {1, 2}; // expected-note {{declared here}}
> + constexpr Mut3 m3 = {1, 2}; // expected-note {{declared here}}
> + struct A {
> + void f() {
> + static_assert(nm1.a == 1, "");
> + static_assert(nm2.m.a == 1, "");
> + static_assert(nm3.a == 1, "");
> + // Can't even access a non-mutable member of a variable
> containing mutable fields.
> + static_assert(m1.a == 1, ""); // expected-error {{enclosing
> function}}
> + static_assert(m2.m.a == 1, ""); // expected-error {{enclosing
> function}}
> + static_assert(m3.a == 1, ""); // expected-error {{enclosing
> function}}
> + }
> + };
> + }
> +#endif
> +
> + void ellipsis() {
> + void ellipsis(...);
> + struct A {};
> + const int n = 0;
> +#if __cplusplus >= 201103L
> + constexpr
> +#endif
> + A a = {}; // expected-note {{here}}
> + struct B {
> + void f() {
> + ellipsis(n);
> + // Even though this is technically modelled as an lvalue-to-rvalue
> + // conversion, it calls a constructor and binds 'a' to a
> reference, so
> + // it results in an odr-use.
> + ellipsis(a); // expected-error {{enclosing function}}
> + }
> + };
> + }
> +
> +#if __cplusplus >= 201103L
> + void volatile_lval() {
> + struct A { int n; };
> + constexpr A a = {0}; // expected-note {{here}}
> + struct B {
> + void f() {
> + // An lvalue-to-rvalue conversion of a volatile lvalue always
> results
> + // in odr-use.
> + int A::*p = &A::n;
> + int x = a.*p;
> + volatile int A::*q = p;
> + int y = a.*q; // expected-error {{enclosing function}}
> + }
> + };
> + }
> +#endif
> +
> + void discarded_lval() {
> + struct A { int x; mutable int y; volatile int z; };
> + A a; // expected-note 1+{{here}}
> + int &r = a.x; // expected-note {{here}}
> + struct B {
> + void f() {
> + a.x; // expected-warning {{unused}}
> + a.*&A::x; // expected-warning {{unused}}
> + true ? a.x : a.y; // expected-warning {{unused}}
> + (void)a.x;
> + a.x, discarded_lval(); // expected-warning {{unused}}
> +#if 1 // FIXME: These errors are all incorrect; the above code is valid.
> + // expected-error at -6 {{enclosing function}}
> + // expected-error at -6 {{enclosing function}}
> + // expected-error at -6 2{{enclosing function}}
> + // expected-error at -6 {{enclosing function}}
> + // expected-error at -6 {{enclosing function}}
> +#endif
> +
> + // 'volatile' qualifier triggers an lvalue-to-rvalue conversion.
> + a.z; // expected-error {{enclosing function}}
> +#if __cplusplus < 201103L
> + // expected-warning at -2 {{assign into a variable}}
> +#endif
> +
> + // References always get "loaded" to determine what they
> reference,
> + // even if the result is discarded.
> + r; // expected-error {{enclosing function}} expected-warning
> {{unused}}
> + }
> + };
> + }
> +
> + namespace dr_example_1 {
> + extern int globx;
> + int main() {
> + const int &x = globx;
> + struct A {
> +#if __cplusplus < 201103L
> + // expected-error at +2 {{enclosing function}} expected-note at -3
> {{here}}
> +#endif
> + const int *foo() { return &x; }
> + } a;
> + return *a.foo();
> + }
> + }
> +
> +#if __cplusplus >= 201103L
> + namespace dr_example_2 {
> + struct A {
> + int q;
> + constexpr A(int q) : q(q) {}
> + constexpr A(const A &a) : q(a.q * 2) {} // (note, not called)
> + };
> +
> + int main(void) {
> + constexpr A a(42);
> + constexpr int aq = a.q;
> + struct Q {
> + int foo() { return a.q; }
> + } q;
> + return q.foo();
> + }
> +
> + // Checking odr-use does not invent an lvalue-to-rvalue conversion
> (and
> + // hence copy construction) on the potential result variable.
> + struct B {
> + int b = 42;
> + constexpr B() {}
> + constexpr B(const B&) = delete;
> + };
> + void f() {
> + constexpr B b;
> + struct Q {
> + constexpr int foo() const { return b.b; }
> + };
> + static_assert(Q().foo() == 42, "");
> + }
> + }
> +#endif
> +}
> +
> namespace dr2094 { // dr2094: 5
> struct A { int n; };
> struct B { volatile int n; };
>
> Modified: cfe/trunk/test/CXX/drs/dr21xx.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr21xx.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/CXX/drs/dr21xx.cpp (original)
> +++ cfe/trunk/test/CXX/drs/dr21xx.cpp Thu Jun 13 12:00:16 2019
> @@ -8,6 +8,19 @@
> #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
> #endif
>
> +namespace dr2103 { // dr2103: yes
> + void f() {
> + int a;
> + int &r = a; // expected-note {{here}}
> + struct Inner {
> + void f() {
> + int &s = r; // expected-error {{enclosing function}}
> + (void)s;
> + }
> + };
> + }
> +}
> +
> namespace dr2120 { // dr2120: 7
> struct A {};
> struct B : A {};
> @@ -19,6 +32,19 @@ namespace dr2120 { // dr2120: 7
> static_assert(!__is_standard_layout(E), "");
> }
>
> +namespace dr2170 { // dr2170: 9
> +#if __cplusplus >= 201103L
> + void f() {
> + constexpr int arr[3] = {1, 2, 3}; // expected-note {{here}}
> + struct S {
> + int get(int n) { return arr[n]; }
> + const int &get_ref(int n) { return arr[n]; } // expected-error
> {{enclosing function}}
> + // FIXME: expected-warning at -1 {{reference to stack}}
> + };
> + }
> +#endif
> +}
> +
> namespace dr2180 { // dr2180: yes
> class A {
> A &operator=(const A &); // expected-note 0-2{{here}}
>
> Modified: cfe/trunk/test/CXX/drs/dr23xx.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr23xx.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/CXX/drs/dr23xx.cpp (original)
> +++ cfe/trunk/test/CXX/drs/dr23xx.cpp Thu Jun 13 12:00:16 2019
> @@ -1,13 +1,45 @@
> -// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors
> -// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors
> -// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors
> -// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors
> -// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors
> +// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors 2>&1 | FileCheck %s
> +// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors 2>&1 | FileCheck %s
> +// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors 2>&1 | FileCheck %s
> +// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors 2>&1 | FileCheck %s
> +// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions
> -pedantic-errors 2>&1 | FileCheck %s
>
> #if __cplusplus <= 201103L
> // expected-no-diagnostics
> #endif
>
> +namespace dr2353 { // dr2353: 9
> + struct X {
> + static const int n = 0;
> + };
> +
> + // CHECK: FunctionDecl {{.*}} use
> + int use(X x) {
> + // CHECK: MemberExpr {{.*}} .n
> + // CHECK-NOT: non_odr_use
> + // CHECK: DeclRefExpr {{.*}} 'x'
> + // CHECK-NOT: non_odr_use
> + return *&x.n;
> + }
> +#pragma clang __debug dump use
> +
> + // CHECK: FunctionDecl {{.*}} not_use
> + int not_use(X x) {
> + // CHECK: MemberExpr {{.*}} .n {{.*}} non_odr_use_constant
> + // CHECK: DeclRefExpr {{.*}} 'x'
> + return x.n;
> + }
> +#pragma clang __debug dump not_use
> +
> + // CHECK: FunctionDecl {{.*}} not_use_2
> + int not_use_2(X *x) {
> + // CHECK: MemberExpr {{.*}} ->n {{.*}} non_odr_use_constant
> + // CHECK: DeclRefExpr {{.*}} 'x'
> + return x->n;
> + }
> +#pragma clang __debug dump not_use_2
> +}
> +
> namespace dr2387 { // dr2387: 9
> #if __cplusplus >= 201402L
> template<int> int a = 0;
>
> Modified: cfe/trunk/test/CXX/drs/dr6xx.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr6xx.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/CXX/drs/dr6xx.cpp (original)
> +++ cfe/trunk/test/CXX/drs/dr6xx.cpp Thu Jun 13 12:00:16 2019
> @@ -1132,3 +1132,20 @@ namespace dr692 { // dr692: no
> template void f(int*); // expected-error {{ambiguous}}
> }
> }
> +
> +namespace dr696 { // dr696: yes
> + void f(const int*);
> + void g() {
> + const int N = 10; // expected-note 1+{{here}}
> + struct A {
> + void h() {
> + int arr[N]; (void)arr;
> + f(&N); // expected-error {{declared in enclosing}}
> + }
> + };
> +#if __cplusplus >= 201103L
> + (void) [] { int arr[N]; (void)arr; };
> + (void) [] { f(&N); }; // expected-error {{cannot be implicitly
> captured}} expected-note {{here}}
> +#endif
> + }
> +}
>
> Modified: cfe/trunk/test/CXX/drs/dr7xx.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr7xx.cpp?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/CXX/drs/dr7xx.cpp (original)
> +++ cfe/trunk/test/CXX/drs/dr7xx.cpp Thu Jun 13 12:00:16 2019
> @@ -17,6 +17,42 @@ namespace dr705 { // dr705: yes
> }
> }
>
> +namespace dr712 { // dr712: partial
> + void use(int);
> + void f() {
> + const int a = 0; // expected-note 5{{here}}
> + struct X {
> + void g(bool cond) {
> + use(a);
> + use((a));
> + use(cond ? a : a);
> + use((cond, a)); // expected-warning 2{{unused}} FIXME: should
> only warn once
> +
> + (void)a; // FIXME: expected-error {{declared in enclosing}}
> + (void)(a); // FIXME: expected-error {{declared in enclosing}}
> + (void)(cond ? a : a); // FIXME: expected-error 2{{declared in
> enclosing}}
> + (void)(cond, a); // FIXME: expected-error {{declared in
> enclosing}} expected-warning {{unused}}
> + }
> + };
> + }
> +
> +#if __cplusplus >= 201103L
> + void g() {
> + struct A { int n; };
> + constexpr A a = {0}; // expected-note 2{{here}}
> + struct X {
> + void g(bool cond) {
> + use(a.n);
> + use(a.*&A::n);
> +
> + (void)a.n; // FIXME: expected-error {{declared in enclosing}}
> + (void)(a.*&A::n); // FIXME: expected-error {{declared in
> enclosing}}
> + }
> + };
> + }
> +#endif
> +}
> +
> namespace dr727 { // dr727: partial
> struct A {
> template<typename T> struct C; // expected-note 6{{here}}
>
> Added: cfe/trunk/test/CodeGenCXX/no-odr-use.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no-odr-use.cpp?rev=363295&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/CodeGenCXX/no-odr-use.cpp (added)
> +++ cfe/trunk/test/CodeGenCXX/no-odr-use.cpp Thu Jun 13 12:00:16 2019
> @@ -0,0 +1,27 @@
> +// RUN: %clang_cc1 -emit-llvm -o - -triple x86_64-linux-gnu %s |
> FileCheck %s
> +
> +// CHECK: @__const._Z1fi.a = private unnamed_addr constant {{.*}} { i32
> 1, [2 x i32] [i32 2, i32 3], [3 x i32] [i32 4, i32 5, i32 6] }
> +
> +struct A { int x, y[2]; int arr[3]; };
> +// CHECK-LABEL: define i32 @_Z1fi(
> +int f(int i) {
> + // CHECK: call void {{.*}}memcpy{{.*}}({{.*}}, {{.*}} @__const._Z1fi.a
> + constexpr A a = {1, 2, 3, 4, 5, 6};
> +
> + // CHECK-LABEL: define {{.*}}@"_ZZ1fiENK3$_0clEiM1Ai"(
> + return [] (int n, int A::*p) {
> + // CHECK: br i1
> + return (n >= 0
> + // CHECK: getelementptr inbounds [3 x i32], [3 x i32]*
> getelementptr inbounds ({{.*}} @__const._Z1fi.a, i32 0, i32 2), i64 0, i64 %
> + ? a.arr[n]
> + // CHECK: br i1
> + : (n == -1
> + // CHECK: getelementptr inbounds i8, i8* bitcast ({{.*}}
> @__const._Z1fi.a to i8*), i64 %
> + // CHECK: bitcast i8* %{{.*}} to i32*
> + // CHECK: load i32
> + ? a.*p
> + // CHECK: getelementptr inbounds [2 x i32], [2 x i32]*
> getelementptr inbounds ({{.*}} @__const._Z1fi.a, i32 0, i32 1), i64 0, i64 %
> + // CHECK: load i32
> + : a.y[2 - n]));
> + }(i, &A::x);
> +}
>
> Modified: cfe/trunk/www/cxx_dr_status.html
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_status.html?rev=363295&r1=363294&r2=363295&view=diff
>
> ==============================================================================
> --- cfe/trunk/www/cxx_dr_status.html (original)
> +++ cfe/trunk/www/cxx_dr_status.html Thu Jun 13 12:00:16 2019
> @@ -4219,7 +4219,7 @@ and <I>POD class</I></td>
> <td><a href="http://wg21.link/cwg696">696</a></td>
> <td>C++11</td>
> <td>Use of block-scope constants in local classes</td>
> - <td class="none" align="center">Unknown</td>
> + <td class="full" align="center">Yes</td>
> </tr>
> <tr class="open" id="697">
> <td><a href="http://wg21.link/cwg697">697</a></td>
> @@ -4315,7 +4315,7 @@ and <I>POD class</I></td>
> <td><a href="http://wg21.link/cwg712">712</a></td>
> <td>CD3</td>
> <td>Are integer constant operands of a <I>conditional-expression</I>
> “used?”</td>
> - <td class="none" align="center">Unknown</td>
> + <td class="partial" align="center">Partial</td>
> </tr>
> <tr id="713">
> <td><a href="http://wg21.link/cwg713">713</a></td>
> @@ -12313,7 +12313,7 @@ and <I>POD class</I></td>
> <td><a href="http://wg21.link/cwg2083">2083</a></td>
> <td>DR</td>
> <td>Incorrect cases of odr-use</td>
> - <td class="none" align="center">Unknown</td>
> + <td class="partial" align="center">Partial</td>
> </tr>
> <tr id="2084">
> <td><a href="http://wg21.link/cwg2084">2084</a></td>
> @@ -12433,7 +12433,7 @@ and <I>POD class</I></td>
> <td><a href="http://wg21.link/cwg2103">2103</a></td>
> <td>DR</td>
> <td>Lvalue-to-rvalue conversion is irrelevant in odr-use of a
> reference</td>
> - <td class="none" align="center">Unknown</td>
> + <td class="full" align="center">Yes</td>
> </tr>
> <tr id="2104">
> <td><a href="http://wg21.link/cwg2104">2104</a></td>
> @@ -12835,7 +12835,7 @@ and <I>POD class</I></td>
> <td><a href="http://wg21.link/cwg2170">2170</a></td>
> <td>DR</td>
> <td>Unclear definition of odr-use for arrays</td>
> - <td class="none" align="center">Unknown</td>
> + <td class="svn" align="center">SVN</td>
> </tr>
> <tr id="2171">
> <td><a href="http://wg21.link/cwg2171">2171</a></td>
> @@ -13933,7 +13933,7 @@ and <I>POD class</I></td>
> <td><a href="http://wg21.link/cwg2353">2353</a></td>
> <td>DR</td>
> <td>Potential results of a member access expression for a static data
> member</td>
> - <td class="none" align="center">Unknown</td>
> + <td class="svn" align="center">SVN</td>
> </tr>
> <tr id="2354">
> <td><a href="http://wg21.link/cwg2354">2354</a></td>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20190614/931942ca/attachment-0001.html>
More information about the cfe-commits
mailing list