diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4575,6 +4575,7 @@ friend class ASTStmtWriter; llvm::PointerUnion State; + bool CanIgnore = false; public: MaterializeTemporaryExpr(QualType T, Expr *Temporary, @@ -4593,6 +4594,10 @@ : State.get()->getTemporaryExpr()); } + // Wether the MaterializeTemporaryExpr can be ignored. + bool canIgnore() const { return CanIgnore; } + void setCanIgnore(bool Val = true) { CanIgnore = Val; } + /// Retrieve the storage duration for the materialized temporary. StorageDuration getStorageDuration() const { return State.is() ? SD_FullExpression diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1353,6 +1353,8 @@ bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false; + bool IsCXXForRangeVariable = false; + // When evaluating immediate functions in the initializer of a default // argument or default member initializer, this is the declaration whose // default initializer is being evaluated and the location of the call @@ -9400,6 +9402,9 @@ /// We are building deduction guides for a class. BuildingDeductionGuides, + + /// We are building an C++ for-range variable. + BuiltingCXXForRangeVariable } Kind; /// Was the enclosing context a non-instantiation SFINAE context? diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -193,6 +193,11 @@ return EmitIgnoredConditionalOperator(CondOp); } + if (auto *MTE = dyn_cast(E)) { + if (MTE->canIgnore()) + E = MTE->getSubExpr(); + } + // Just emit it as an l-value and drop the result. EmitLValue(E); } diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -414,6 +414,8 @@ return "BuildingBuiltinDumpStructCall"; case CodeSynthesisContext::BuildingDeductionGuides: return "BuildingDeductionGuides"; + case CodeSynthesisContext::BuiltingCXXForRangeVariable: + return "BuiltingCXXForRangeVariable"; } return ""; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8189,16 +8189,17 @@ // [Except in specific positions,] an lvalue that does not have // array type is converted to the value stored in the // designated object (and is no longer an lvalue). - if (E->isPRValue()) { - // In C, function designators (i.e. expressions of function type) - // are r-values, but we still want to do function-to-pointer decay - // on them. This is both technically correct and convenient for - // some clients. - if (!getLangOpts().CPlusPlus && E->getType()->isFunctionType()) - return DefaultFunctionArrayConversion(E); - - return E; - } + // if ( { + // In C, function designators (i.e. expressions of function type) + // are r-values, but we still want to do function-to-pointer decay + // on them. This is both technically correct and convenient for + // some clients. + if (E->isPRValue() && !getLangOpts().CPlusPlus && + E->getType()->isFunctionType()) + return DefaultFunctionArrayConversion(E); + + // return E; + // } if (getLangOpts().CPlusPlus) { // The C++11 standard defines the notion of a discarded-value expression; @@ -8225,6 +8226,17 @@ // just clutter. // FIXME: We don't emit lifetime markers for the temporaries due to this. // FIXME: Do any other AST consumers care about this? + + // 6.7.7.2.6 - when a prvalue that has type other than cv void appears as + // a discarded-value expression ([expr.context]). + if (getLangOpts().CPlusPlus17 && E->isPRValue() && + !E->getType()->isVoidType()) { + ExprResult Res = TemporaryMaterializationConversion(E); + if (Res.isInvalid()) + return E; + E = Res.get(); + dyn_cast(E)->setCanIgnore(); + } return E; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7355,15 +7355,15 @@ }); } -static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, - Expr *Init, LocalVisitor Visit, - bool RevisitSubinits, - bool EnableLifetimeWarnings); +static void +visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, + LocalVisitor Visit, bool RevisitSubinits, + bool EnableLifetimeWarnings, + bool isCXXForRangeVariable = false); -static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, - Expr *Init, ReferenceKind RK, - LocalVisitor Visit, - bool EnableLifetimeWarnings); +static void visitLocalsRetainedByReferenceBinding( + IndirectLocalPath &Path, Expr *Init, ReferenceKind RK, LocalVisitor Visit, + bool EnableLifetimeWarnings, bool isCXXForRangeVariable = false); template static bool isRecordWithAttr(QualType Type) { if (auto *RD = Type->getAsCXXRecordDecl()) @@ -7543,7 +7543,8 @@ } static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call, - LocalVisitor Visit) { + LocalVisitor Visit, + bool isCXXForRangeVariable = false) { const FunctionDecl *Callee; ArrayRef Args; @@ -7584,7 +7585,8 @@ for (unsigned I = 0, N = std::min(Callee->getNumParams(), Args.size()); I != N; ++I) { - if (Callee->getParamDecl(I)->hasAttr()) + if (Callee->getParamDecl(I)->hasAttr() || + isCXXForRangeVariable) VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]); } } @@ -7594,7 +7596,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, Expr *Init, ReferenceKind RK, LocalVisitor Visit, - bool EnableLifetimeWarnings) { + bool EnableLifetimeWarnings, + bool isCXXForRangeVariable) { RevertToOldSizeRAII RAII(Path); // Walk past any constructs which we can lifetime-extend across. @@ -7613,7 +7616,15 @@ // Step over any subobject adjustments; we may have a materialized // temporary inside them. - Init = const_cast(Init->skipRValueSubobjectAdjustments()); + SmallVector CommaLHS; + SmallVector Adjustments; + Init = const_cast( + Init->skipRValueSubobjectAdjustments(CommaLHS, Adjustments)); + + for (auto *LHS : CommaLHS) + visitLocalsRetainedByReferenceBinding(Path, const_cast(LHS), + RK_ReferenceBinding, Visit, + EnableLifetimeWarnings); // Per current approach for DR1376, look through casts to reference type // when performing lifetime extension. @@ -7653,7 +7664,8 @@ if (isa(Init)) { if (EnableLifetimeWarnings) handleGslAnnotatedTypes(Path, Init, Visit); - return visitLifetimeBoundArguments(Path, Init, Visit); + return visitLifetimeBoundArguments(Path, Init, Visit, + isCXXForRangeVariable); } switch (Init->getStmtClass()) { @@ -7680,6 +7692,17 @@ break; } + case Stmt::BinaryOperatorClass: { + const BinaryOperator *BO = cast(Init); + visitLocalsRetainedByReferenceBinding( + Path, BO->getLHS(), RK_ReferenceBinding, Visit, EnableLifetimeWarnings, + isCXXForRangeVariable); + visitLocalsRetainedByReferenceBinding( + Path, BO->getRHS(), RK_ReferenceBinding, Visit, EnableLifetimeWarnings, + isCXXForRangeVariable); + break; + } + case Stmt::UnaryOperatorClass: { // The only unary operator that make sense to handle here // is Deref. All others don't resolve to a "name." This includes @@ -7722,7 +7745,8 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, LocalVisitor Visit, bool RevisitSubinits, - bool EnableLifetimeWarnings) { + bool EnableLifetimeWarnings, + bool isCXXForRangeVariable) { RevertToOldSizeRAII RAII(Path); Expr *Old; @@ -8068,6 +8092,10 @@ LifetimeResult LR = getEntityLifetime(&Entity); LifetimeKind LK = LR.getInt(); const InitializedEntity *ExtendingEntity = LR.getPointer(); + bool isInitCXXForRangeVarDecl = + !CodeSynthesisContexts.empty() && + CodeSynthesisContexts.back().Kind == + CodeSynthesisContext::BuiltingCXXForRangeVariable; // If this entity doesn't have an interesting lifetime, don't bother looking // for temporaries within its initializer. @@ -8081,6 +8109,13 @@ auto *MTE = dyn_cast(L); + if (getLangOpts().CPlusPlus23 && MTE && isInitCXXForRangeVarDecl) { + MTE->setCanIgnore(false); + MTE->setExtendingDecl(ExtendingEntity->getDecl(), + ExtendingEntity->allocateManglingNumber()); + return true; + } + bool IsGslPtrInitWithGslTempOwner = false; bool IsLocalGslOwner = false; if (pathOnlyInitializesGslPointer(Path)) { @@ -8328,12 +8363,13 @@ diag::warn_dangling_lifetime_pointer, SourceLocation()); llvm::SmallVector Path; if (Init->isGLValue()) - visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding, - TemporaryVisitor, - EnableLifetimeWarnings); + visitLocalsRetainedByReferenceBinding( + Path, Init, RK_ReferenceBinding, TemporaryVisitor, + EnableLifetimeWarnings, isInitCXXForRangeVarDecl); else visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false, - EnableLifetimeWarnings); + EnableLifetimeWarnings, + isInitCXXForRangeVarDecl); } static void DiagnoseNarrowingInInitList(Sema &S, diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2522,6 +2522,12 @@ } } + // Register a note to explain why we're performing the call. + CodeSynthesisContext Ctx; + Ctx.Kind = CodeSynthesisContext::BuiltingCXXForRangeVariable; + Ctx.PointOfInstantiation = ForLoc; + pushCodeSynthesisContext(Ctx); + // Build auto && __range = range-init // Divide by 2, since the variables are in the inner scope (loop body). const auto DepthStr = std::to_string(S->getDepth() / 2); @@ -2529,8 +2535,10 @@ VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc, Context.getAutoRRefDeductType(), std::string("__range") + DepthStr); - if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc, - diag::err_for_range_deduction_failure)) { + bool Res = FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc, + diag::err_for_range_deduction_failure); + popCodeSynthesisContext(); + if (Res) { ActOnInitializerError(LoopVar); return StmtError(); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -410,6 +410,7 @@ case BuildingBuiltinDumpStructCall: case LambdaExpressionSubstitution: case BuildingDeductionGuides: + case BuiltingCXXForRangeVariable: return false; // This function should never be called when Kind's value is Memoization. @@ -1009,7 +1010,12 @@ << convertCallArgsToString( *this, llvm::ArrayRef(Active->CallArgs, Active->NumCallArgs)); break; - + case CodeSynthesisContext::BuiltingCXXForRangeVariable: + Diags.Report(Active->PointOfInstantiation, + diag::note_building_builtin_dump_struct_call) + << convertCallArgsToString( + *this, llvm::ArrayRef(Active->CallArgs, Active->NumCallArgs)); + break; case CodeSynthesisContext::Memoization: break; @@ -1138,6 +1144,7 @@ case CodeSynthesisContext::MarkingClassDllexported: case CodeSynthesisContext::BuildingBuiltinDumpStructCall: case CodeSynthesisContext::BuildingDeductionGuides: + case CodeSynthesisContext::BuiltingCXXForRangeVariable: // This happens in a context unrelated to template instantiation, so // there is no SFINAE. return std::nullopt;