diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h --- a/clang/include/clang/AST/StmtCXX.h +++ b/clang/include/clang/AST/StmtCXX.h @@ -327,7 +327,6 @@ Allocate, ///< Coroutine frame memory allocation. Deallocate, ///< Coroutine frame memory deallocation. ReturnValue, ///< Return value for thunk function: p.get_return_object(). - ResultDecl, ///< Declaration holding the result of get_return_object. ReturnStmt, ///< Return statement for the thunk function. ReturnStmtOnAllocFailure, ///< Return statement if allocation failed. FirstParamMove ///< First offset for move construction of parameter copies. @@ -354,7 +353,6 @@ Expr *Allocate = nullptr; Expr *Deallocate = nullptr; Expr *ReturnValue = nullptr; - Stmt *ResultDecl = nullptr; Stmt *ReturnStmt = nullptr; Stmt *ReturnStmtOnAllocFailure = nullptr; ArrayRef ParamMoves; @@ -409,7 +407,11 @@ Expr *getReturnValueInit() const { return cast(getStoredStmts()[SubStmt::ReturnValue]); } - Stmt *getResultDecl() const { return getStoredStmts()[SubStmt::ResultDecl]; } + Expr *getReturnValue() const { + assert(getReturnStmt()); + auto *RS = cast(getReturnStmt()); + return RS->getRetValue(); + } Stmt *getReturnStmt() const { return getStoredStmts()[SubStmt::ReturnStmt]; } Stmt *getReturnStmtOnAllocFailure() const { return getStoredStmts()[SubStmt::ReturnStmtOnAllocFailure]; diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp --- a/clang/lib/AST/StmtCXX.cpp +++ b/clang/lib/AST/StmtCXX.cpp @@ -118,7 +118,6 @@ SubStmts[CoroutineBodyStmt::Allocate] = Args.Allocate; SubStmts[CoroutineBodyStmt::Deallocate] = Args.Deallocate; SubStmts[CoroutineBodyStmt::ReturnValue] = Args.ReturnValue; - SubStmts[CoroutineBodyStmt::ResultDecl] = Args.ResultDecl; SubStmts[CoroutineBodyStmt::ReturnStmt] = Args.ReturnStmt; SubStmts[CoroutineBodyStmt::ReturnStmtOnAllocFailure] = Args.ReturnStmtOnAllocFailure; diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -465,72 +465,6 @@ }; } -namespace { -struct GetReturnObjectManager { - CodeGenFunction &CGF; - CGBuilderTy &Builder; - const CoroutineBodyStmt &S; - - Address GroActiveFlag; - CodeGenFunction::AutoVarEmission GroEmission; - - GetReturnObjectManager(CodeGenFunction &CGF, const CoroutineBodyStmt &S) - : CGF(CGF), Builder(CGF.Builder), S(S), GroActiveFlag(Address::invalid()), - GroEmission(CodeGenFunction::AutoVarEmission::invalid()) {} - - // The gro variable has to outlive coroutine frame and coroutine promise, but, - // it can only be initialized after coroutine promise was created, thus, we - // split its emission in two parts. EmitGroAlloca emits an alloca and sets up - // cleanups. Later when coroutine promise is available we initialize the gro - // and sets the flag that the cleanup is now active. - - void EmitGroAlloca() { - auto *GroDeclStmt = dyn_cast(S.getResultDecl()); - if (!GroDeclStmt) { - // If get_return_object returns void, no need to do an alloca. - return; - } - - auto *GroVarDecl = cast(GroDeclStmt->getSingleDecl()); - - // Set GRO flag that it is not initialized yet - GroActiveFlag = - CGF.CreateTempAlloca(Builder.getInt1Ty(), CharUnits::One(), "gro.active"); - Builder.CreateStore(Builder.getFalse(), GroActiveFlag); - - GroEmission = CGF.EmitAutoVarAlloca(*GroVarDecl); - - // Remember the top of EHStack before emitting the cleanup. - auto old_top = CGF.EHStack.stable_begin(); - CGF.EmitAutoVarCleanups(GroEmission); - auto top = CGF.EHStack.stable_begin(); - - // Make the cleanup conditional on gro.active - for (auto b = CGF.EHStack.find(top), e = CGF.EHStack.find(old_top); - b != e; b++) { - if (auto *Cleanup = dyn_cast(&*b)) { - assert(!Cleanup->hasActiveFlag() && "cleanup already has active flag?"); - Cleanup->setActiveFlag(GroActiveFlag); - Cleanup->setTestFlagInEHCleanup(); - Cleanup->setTestFlagInNormalCleanup(); - } - } - } - - void EmitGroInit() { - if (!GroActiveFlag.isValid()) { - // No Gro variable was allocated. Simply emit the call to - // get_return_object. - CGF.EmitStmt(S.getResultDecl()); - return; - } - - CGF.EmitAutoVarInit(GroEmission); - Builder.CreateStore(Builder.getTrue(), GroActiveFlag); - } -}; -} - static void emitBodyAndFallthrough(CodeGenFunction &CGF, const CoroutineBodyStmt &S, Stmt *Body) { CGF.EmitStmt(Body); @@ -597,13 +531,6 @@ CGM.getIntrinsic(llvm::Intrinsic::coro_begin), {CoroId, Phi}); CurCoro.Data->CoroBegin = CoroBegin; - // We need to emit `get_­return_­object` first. According to: - // [dcl.fct.def.coroutine]p7 - // The call to get_­return_­object is sequenced before the call to - // initial_­suspend and is invoked at most once. - GetReturnObjectManager GroManager(*this, S); - GroManager.EmitGroAlloca(); - CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB); { CGDebugInfo *DI = getDebugInfo(); @@ -641,8 +568,23 @@ // promise local variable was not emitted yet. CoroId->setArgOperand(1, PromiseAddrVoidPtr); - // Now we have the promise, initialize the GRO - GroManager.EmitGroInit(); + // ReturnValue should be valid as long as the coroutine's return type + // is not void. The assertion could help us to reduce the check later. + assert(ReturnValue.isValid() == (bool)S.getReturnStmt()); + // Now we have the promise, initialize the GRO. + // We need to emit `get_return_object` first. According to: + // [dcl.fct.def.coroutine]p7 + // The call to get_return_­object is sequenced before the call to + // initial_suspend and is invoked at most once. + // + // So we couldn't emit return value when we emit return statment, + // otherwise the call to get_return_object wouldn't be in front + // of initial_suspend. + if (ReturnValue.isValid()) { + EmitAnyExprToMem(S.getReturnValue(), ReturnValue, + S.getReturnValue()->getType().getQualifiers(), + /*IsInit*/ true); + } EHStack.pushCleanup(EHCleanup); @@ -705,8 +647,12 @@ llvm::Function *CoroEnd = CGM.getIntrinsic(llvm::Intrinsic::coro_end); Builder.CreateCall(CoroEnd, {NullPtr, Builder.getFalse()}); - if (Stmt *Ret = S.getReturnStmt()) + if (Stmt *Ret = S.getReturnStmt()) { + // Since we already emitted the return value above, so we shouldn't + // emit it again here. + cast(Ret)->setRetValue(nullptr); EmitStmt(Ret); + } // LLVM require the frontend to add the function attribute. See // Coroutines.rst. diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -1577,7 +1577,6 @@ if (Res.isInvalid()) return false; - this->ResultDecl = Res.get(); return true; } @@ -1590,51 +1589,11 @@ return false; } - auto *GroDecl = VarDecl::Create( - S.Context, &FD, FD.getLocation(), FD.getLocation(), - &S.PP.getIdentifierTable().get("__coro_gro"), GroType, - S.Context.getTrivialTypeSourceInfo(GroType, Loc), SC_None); - GroDecl->setImplicit(); - - S.CheckVariableDeclarationType(GroDecl); - if (GroDecl->isInvalidDecl()) - return false; - - InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl); - ExprResult Res = - S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue); - if (Res.isInvalid()) - return false; - - Res = S.ActOnFinishFullExpr(Res.get(), /*DiscardedValue*/ false); - if (Res.isInvalid()) - return false; - - S.AddInitializerToDecl(GroDecl, Res.get(), - /*DirectInit=*/false); - - S.FinalizeDeclaration(GroDecl); - - // Form a declaration statement for the return declaration, so that AST - // visitors can more easily find it. - StmtResult GroDeclStmt = - S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(GroDecl), Loc, Loc); - if (GroDeclStmt.isInvalid()) - return false; - - this->ResultDecl = GroDeclStmt.get(); - - ExprResult declRef = S.BuildDeclRefExpr(GroDecl, GroType, VK_LValue, Loc); - if (declRef.isInvalid()) - return false; - - StmtResult ReturnStmt = S.BuildReturnStmt(Loc, declRef.get()); + StmtResult ReturnStmt = S.BuildReturnStmt(Loc, ReturnValue); if (ReturnStmt.isInvalid()) { noteMemberDeclaredHere(S, ReturnValue, Fn); return false; } - if (cast(ReturnStmt.get())->getNRVOCandidate() == GroDecl) - GroDecl->setNRVOVariable(true); this->ReturnStmt = ReturnStmt.get(); return true; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7903,12 +7903,6 @@ return StmtError(); Builder.Deallocate = DeallocRes.get(); - assert(S->getResultDecl() && "ResultDecl must already be built"); - StmtResult ResultDecl = getDerived().TransformStmt(S->getResultDecl()); - if (ResultDecl.isInvalid()) - return StmtError(); - Builder.ResultDecl = ResultDecl.get(); - if (auto *ReturnStmt = S->getReturnStmt()) { StmtResult Res = getDerived().TransformStmt(ReturnStmt); if (Res.isInvalid()) diff --git a/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp --- a/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp +++ b/clang/test/CodeGenCoroutines/coro-alloc-exp-namespace.cpp @@ -226,7 +226,6 @@ // CHECK-LABEL: f4( extern "C" int f4(promise_on_alloc_failure_tag) { // CHECK: %[[RetVal:.+]] = alloca i32 - // CHECK: %[[Gro:.+]] = alloca i32 // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() // CHECK: %[[MEM:.+]] = call noalias noundef i8* @_ZnwmRKSt9nothrow_t(i64 noundef %[[SIZE]], %"struct.std::nothrow_t"* noundef nonnull align 1 dereferenceable(1) @_ZStL7nothrow) @@ -240,13 +239,6 @@ // CHECK: [[OKBB]]: // CHECK: %[[OkRet:.+]] = call noundef i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv( - // CHECK: store i32 %[[OkRet]], i32* %[[Gro]] - - // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]] - // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]] - // CHECK-NEXT: %[[Gro_CAST:.+]] = bitcast i32* %[[Gro]] to i8* - // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %[[Gro_CAST]]) #2 - // CHECK-NEXT: br label %[[RetBB]] // CHECK: [[RetBB]]: // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4 diff --git a/clang/test/CodeGenCoroutines/coro-alloc.cpp b/clang/test/CodeGenCoroutines/coro-alloc.cpp --- a/clang/test/CodeGenCoroutines/coro-alloc.cpp +++ b/clang/test/CodeGenCoroutines/coro-alloc.cpp @@ -224,7 +224,6 @@ // CHECK-LABEL: f4( extern "C" int f4(promise_on_alloc_failure_tag) { // CHECK: %[[RetVal:.+]] = alloca i32 - // CHECK: %[[Gro:.+]] = alloca i32 // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() // CHECK: %[[MEM:.+]] = call noalias noundef i8* @_ZnwmRKSt9nothrow_t(i64 noundef %[[SIZE]], %"struct.std::nothrow_t"* noundef nonnull align 1 dereferenceable(1) @_ZStL7nothrow) @@ -238,13 +237,6 @@ // CHECK: [[OKBB]]: // CHECK: %[[OkRet:.+]] = call noundef i32 @_ZNSt16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv( - // CHECK: store i32 %[[OkRet]], i32* %[[Gro]] - - // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]] - // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]] - // CHECK-NEXT: %[[Gro_CAST:.+]] = bitcast i32* %[[Gro]] to i8* - // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %[[Gro_CAST]]) #2 - // CHECK-NEXT: br label %[[RetBB]] // CHECK: [[RetBB]]: // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4 diff --git a/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp --- a/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro-exp-namespace.cpp @@ -48,14 +48,13 @@ // CHECK: define{{.*}} i32 @_Z1fv( int f() { // CHECK: %[[RetVal:.+]] = alloca i32 - // CHECK: %[[GroActive:.+]] = alloca i1 // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[Size]]) - // CHECK: store i1 false, i1* %[[GroActive]] // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeC1Ev( - // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv( - // CHECK: store i1 true, i1* %[[GroActive]] + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv(%struct.GroType* sret(%struct.GroType) align {{[0-9]+}} %[[GRO:.+]], + // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv({{.*}}[[GRO]] + // CHECK: store i32 %[[Conv]], i32* %[[RetVal]] Cleanup cleanup; doSomething(); @@ -71,18 +70,7 @@ // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free( // CHECK: call void @_ZdlPv(i8* noundef %[[Mem]]) - // Initialize retval from Gro and destroy Gro - - // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv( - // CHECK: store i32 %[[Conv]], i32* %[[RetVal]] - // CHECK: %[[IsActive:.+]] = load i1, i1* %[[GroActive]] - // CHECK: br i1 %[[IsActive]], label %[[CleanupGro:.+]], label %[[Done:.+]] - - // CHECK: [[CleanupGro]]: - // CHECK: call void @_ZN7GroTypeD1Ev( - // CHECK: br label %[[Done]] - - // CHECK: [[Done]]: + // CHECK: coro.ret: // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]] // CHECK: ret i32 %[[LoadRet]] } diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp --- a/clang/test/CodeGenCoroutines/coro-gro.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro.cpp @@ -46,14 +46,13 @@ // CHECK: define{{.*}} i32 @_Z1fv( int f() { // CHECK: %[[RetVal:.+]] = alloca i32 - // CHECK: %[[GroActive:.+]] = alloca i1 // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[Size]]) - // CHECK: store i1 false, i1* %[[GroActive]] // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_typeC1Ev( - // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type17get_return_objectEv( - // CHECK: store i1 true, i1* %[[GroActive]] + // CHECK: call void @_ZNSt16coroutine_traitsIJiEE12promise_type17get_return_objectEv(%struct.GroType* sret(%struct.GroType) align {{[0-9]+}} %[[GRO:.+]], + // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv({{.*}}[[GRO]] + // CHECK: store i32 %[[Conv]], i32* %[[RetVal]] Cleanup cleanup; doSomething(); @@ -69,18 +68,7 @@ // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free( // CHECK: call void @_ZdlPv(i8* noundef %[[Mem]]) - // Initialize retval from Gro and destroy Gro - - // CHECK: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv( - // CHECK: store i32 %[[Conv]], i32* %[[RetVal]] - // CHECK: %[[IsActive:.+]] = load i1, i1* %[[GroActive]] - // CHECK: br i1 %[[IsActive]], label %[[CleanupGro:.+]], label %[[Done:.+]] - - // CHECK: [[CleanupGro]]: - // CHECK: call void @_ZN7GroTypeD1Ev( - // CHECK: br label %[[Done]] - - // CHECK: [[Done]]: + // CHECK: coro.ret: // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]] // CHECK: ret i32 %[[LoadRet]] } diff --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp rename from clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp rename to clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp --- a/clang/test/CodeGenCoroutines/coro-gro-nrvo-exp-namespace.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro2-exp-namespace.cpp @@ -32,16 +32,14 @@ Impl *impl; }; -// Verify that the NRVO is applied to the Gro object. +// Verify that the RVO is applied. // CHECK-LABEL: define{{.*}} void @_Z1fi(%struct.coro* noalias sret(%struct.coro) align 8 %agg.result, i32 noundef %0) coro f(int) { // CHECK: %call = call noalias noundef nonnull i8* @_Znwm( // CHECK-NEXT: br label %[[CoroInit:.*]] // CHECK: {{.*}}[[CoroInit]]: - // CHECK: store i1 false, i1* %gro.active // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret(%struct.coro) align 8 %agg.result - // CHECK-NEXT: store i1 true, i1* %gro.active co_return; } @@ -75,9 +73,7 @@ // CHECK-NEXT: br label %[[RetLabel:.*]] // CHECK: {{.*}}[[InitOnSuccess]]: - // CHECK: store i1 false, i1* %gro.active // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result - // CHECK-NEXT: store i1 true, i1* %gro.active // CHECK: [[RetLabel]]: // CHECK-NEXT: ret void diff --git a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp b/clang/test/CodeGenCoroutines/coro-gro2.cpp rename from clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp rename to clang/test/CodeGenCoroutines/coro-gro2.cpp --- a/clang/test/CodeGenCoroutines/coro-gro-nrvo.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro2.cpp @@ -33,17 +33,15 @@ Impl *impl; }; -// Verify that the NRVO is applied to the Gro object. +// Verify that the RVO is applied. // CHECK-LABEL: define{{.*}} void @_Z1fi(%struct.coro* noalias sret(%struct.coro) align 8 %agg.result, i32 noundef %0) coro f(int) { // CHECK: %call = call noalias noundef nonnull i8* @_Znwm( // CHECK-NEXT: br label %[[CoroInit:.*]] // CHECK: {{.*}}[[CoroInit]]: -// CHECK: store i1 false, i1* %gro.active // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro* sret(%struct.coro) align 8 %agg.result -// CHECK-NEXT: store i1 true, i1* %gro.active - co_return; +co_return; } @@ -64,24 +62,22 @@ Impl *impl; }; -// Verify that the NRVO is applied to the Gro object. +// Verify that the RVO is applied. // CHECK-LABEL: define{{.*}} void @_Z1hi(%struct.coro_two* noalias sret(%struct.coro_two) align 8 %agg.result, i32 noundef %0) - coro_two h(int) { +coro_two h(int) { -// CHECK: %call = call noalias noundef i8* @_ZnwmRKSt9nothrow_t -// CHECK-NEXT: %[[CheckNull:.*]] = icmp ne i8* %call, null -// CHECK-NEXT: br i1 %[[CheckNull]], label %[[InitOnSuccess:.*]], label %[[InitOnFailure:.*]] + // CHECK: %call = call noalias noundef i8* @_ZnwmRKSt9nothrow_t + // CHECK-NEXT: %[[CheckNull:.*]] = icmp ne i8* %call, null + // CHECK-NEXT: br i1 %[[CheckNull]], label %[[InitOnSuccess:.*]], label %[[InitOnFailure:.*]] -// CHECK: {{.*}}[[InitOnFailure]]: -// CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result -// CHECK-NEXT: br label %[[RetLabel:.*]] + // CHECK: {{.*}}[[InitOnFailure]]: + // CHECK-NEXT: call void @{{.*get_return_object_on_allocation_failureEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result + // CHECK-NEXT: br label %[[RetLabel:.*]] -// CHECK: {{.*}}[[InitOnSuccess]]: -// CHECK: store i1 false, i1* %gro.active -// CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result -// CHECK-NEXT: store i1 true, i1* %gro.active + // CHECK: {{.*}}[[InitOnSuccess]]: + // CHECK: call void @{{.*get_return_objectEv}}(%struct.coro_two* sret(%struct.coro_two) align 8 %agg.result -// CHECK: [[RetLabel]]: -// CHECK-NEXT: ret void + // CHECK: [[RetLabel]]: + // CHECK-NEXT: ret void co_return; } diff --git a/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp b/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp --- a/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp +++ b/clang/test/CodeGenCoroutines/coro-promise-dtor-exp-namespace.cpp @@ -31,19 +31,8 @@ } // CHECK-LABEL: define dso_local void @"?f@@YA?AUcoro_t@@XZ"( -// CHECK: %gro.active = alloca i1 -// CHECK: store i1 false, i1* %gro.active // CHECK: invoke noundef %"struct.coro_t::promise_type"* @"??0promise_type@coro_t@@QEAA@XZ"( // CHECK: invoke void @"?get_return_object@promise_type@coro_t@@QEAA?AU2@XZ"( -// CHECK: store i1 true, i1* %gro.active -// CHECK: %[[IS_ACTIVE:.+]] = load i1, i1* %gro.active -// CHECK: br i1 %[[IS_ACTIVE]], label %[[CLEANUP1:.+]], label - -// CHECK: [[CLEANUP1]]: -// CHECK: %[[NRVO:.+]] = load i1, i1* %nrvo -// CHECK: br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]] - -// CHECK: [[DTOR]]: -// CHECK: call void @"??1coro_t@@QEAA@XZ"( +// CHECK: call void @"??1promise_type@coro_t@@QEAA@XZ" diff --git a/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp b/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp --- a/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp +++ b/clang/test/CodeGenCoroutines/coro-promise-dtor.cpp @@ -27,19 +27,8 @@ } // CHECK-LABEL: define dso_local void @"?f@@YA?AUcoro_t@@XZ"( -// CHECK: %gro.active = alloca i1 -// CHECK: store i1 false, i1* %gro.active // CHECK: invoke noundef %"struct.coro_t::promise_type"* @"??0promise_type@coro_t@@QEAA@XZ"( // CHECK: invoke void @"?get_return_object@promise_type@coro_t@@QEAA?AU2@XZ"( -// CHECK: store i1 true, i1* %gro.active -// CHECK: %[[IS_ACTIVE:.+]] = load i1, i1* %gro.active -// CHECK: br i1 %[[IS_ACTIVE]], label %[[CLEANUP1:.+]], label - -// CHECK: [[CLEANUP1]]: -// CHECK: %[[NRVO:.+]] = load i1, i1* %nrvo -// CHECK: br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]] - -// CHECK: [[DTOR]]: -// CHECK: call void @"??1coro_t@@QEAA@XZ"( +// CHECK: call void @"??1promise_type@coro_t@@QEAA@XZ" diff --git a/clang/test/SemaCXX/coroutine-no-move-ctor.cpp b/clang/test/SemaCXX/coroutine-no-move-ctor.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/coroutine-no-move-ctor.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -verify +// expected-no-diagnostics + +#include "Inputs/std-coroutine.h" + +class invoker { +public: + class invoker_promise { + public: + invoker get_return_object() { return invoker{}; } + auto initial_suspend() { return std::suspend_never{}; } + auto final_suspend() noexcept { return std::suspend_never{}; } + void return_void() {} + void unhandled_exception() {} + }; + using promise_type = invoker_promise; + invoker() {} + invoker(const invoker &) = delete; + invoker &operator=(const invoker &) = delete; + invoker(invoker &&) = delete; + invoker &operator=(invoker &&) = delete; +}; + +invoker f() { + co_return; +} diff --git a/clang/test/SemaCXX/coroutines-exp-namespace.cpp b/clang/test/SemaCXX/coroutines-exp-namespace.cpp --- a/clang/test/SemaCXX/coroutines-exp-namespace.cpp +++ b/clang/test/SemaCXX/coroutines-exp-namespace.cpp @@ -939,7 +939,7 @@ extern "C" int f(mismatch_gro_type_tag2) { // cxx2b-error@-1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}} - // cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}} + // cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}} co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}} } diff --git a/clang/test/SemaCXX/coroutines.cpp b/clang/test/SemaCXX/coroutines.cpp --- a/clang/test/SemaCXX/coroutines.cpp +++ b/clang/test/SemaCXX/coroutines.cpp @@ -929,7 +929,7 @@ extern "C" int f(mismatch_gro_type_tag2) { // cxx2b-error@-1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}} - // cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}} + // cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}} co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}} }