On Thu, 13 Jun 2019 at 21:02, Nico Weber <tha...@chromium.org> wrote:
> 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. > Thanks for the revert and the reduced testcase. Fixed and recommitted as r363428-r363430. > Nico > > On Thu, Jun 13, 2019 at 2:56 PM Richard Smith via cfe-commits < > cfe-commits@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@-3 {{in enclosing function}} >> + // expected-error@-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@-6 {{enclosing function}} >> + // expected-error@-6 {{enclosing function}} >> + // expected-error@-6 2{{enclosing function}} >> + // expected-error@-6 {{enclosing function}} >> + // expected-error@-6 {{enclosing function}} >> +#endif >> + >> + // 'volatile' qualifier triggers an lvalue-to-rvalue conversion. >> + a.z; // expected-error {{enclosing function}} >> +#if __cplusplus < 201103L >> + // expected-warning@-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@+2 {{enclosing function}} expected-note@-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@-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@lists.llvm.org >> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >> >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits