forked from OSchip/llvm-project
Revert r300420 - [coroutines] Fix building of new/delete expressions when get_return_object_on_allocation_failure() is present
llvm-svn: 300421
This commit is contained in:
parent
627a63cf50
commit
1bcc937b56
|
@ -8854,11 +8854,6 @@ def err_coroutine_invalid_func_context : Error<
|
|||
def err_implied_coroutine_type_not_found : Error<
|
||||
"%0 type was not found; include <experimental/coroutine> before defining "
|
||||
"a coroutine">;
|
||||
def err_implicit_coroutine_std_nothrow_type_not_found : Error<
|
||||
"std::nothrow was not found; include <new> before defining a coroutine which "
|
||||
"uses get_return_object_on_allocation_failure()">;
|
||||
def err_malformed_std_nothrow : Error<
|
||||
"std::nothrow must be a valid variable declaration">;
|
||||
def err_malformed_std_coroutine_handle : Error<
|
||||
"std::experimental::coroutine_handle must be a class template">;
|
||||
def err_coroutine_handle_missing_member : Error<
|
||||
|
@ -8878,7 +8873,7 @@ def err_coroutine_promise_return_ill_formed : Error<
|
|||
"%0 declares both 'return_value' and 'return_void'">;
|
||||
def note_coroutine_promise_implicit_await_transform_required_here : Note<
|
||||
"call to 'await_transform' implicitly required by 'co_await' here">;
|
||||
def note_coroutine_promise_suspend_implicitly_required : Note<
|
||||
def note_coroutine_promise_call_implicitly_required : Note<
|
||||
"call to '%select{initial_suspend|final_suspend}0' implicitly "
|
||||
"required by the %select{initial suspend point|final suspend point}0">;
|
||||
def err_coroutine_promise_unhandled_exception_required : Error<
|
||||
|
@ -8888,11 +8883,6 @@ def warn_coroutine_promise_unhandled_exception_required_with_exceptions : Warnin
|
|||
InGroup<Coroutine>;
|
||||
def err_coroutine_promise_get_return_object_on_allocation_failure : Error<
|
||||
"%0: 'get_return_object_on_allocation_failure()' must be a static member function">;
|
||||
def err_coroutine_promise_new_requires_nothrow : Error<
|
||||
"%0 is required to have a non-throwing noexcept specification when the promise "
|
||||
"type declares 'get_return_object_on_allocation_failure()'">;
|
||||
def note_coroutine_promise_call_implicitly_required : Note<
|
||||
"call to %0 implicitly required by coroutine function here">;
|
||||
}
|
||||
|
||||
let CategoryName = "Documentation Issue" in {
|
||||
|
|
|
@ -454,7 +454,7 @@ static bool actOnCoroutineBodyStart(Sema &S, Scope *SC, SourceLocation KWLoc,
|
|||
/*IsImplicit*/ true);
|
||||
Suspend = S.ActOnFinishFullExpr(Suspend.get());
|
||||
if (Suspend.isInvalid()) {
|
||||
S.Diag(Loc, diag::note_coroutine_promise_suspend_implicitly_required)
|
||||
S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
|
||||
<< ((Name == "initial_suspend") ? 0 : 1);
|
||||
S.Diag(KWLoc, diag::note_declared_coroutine_here) << Keyword;
|
||||
return StmtError();
|
||||
|
@ -660,39 +660,6 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
|
|||
return Res;
|
||||
}
|
||||
|
||||
/// Look up the std::nothrow object.
|
||||
static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) {
|
||||
NamespaceDecl *Std = S.getStdNamespace();
|
||||
assert(Std && "Should already be diagnosed");
|
||||
|
||||
LookupResult Result(S, &S.PP.getIdentifierTable().get("nothrow"), Loc,
|
||||
Sema::LookupOrdinaryName);
|
||||
if (!S.LookupQualifiedName(Result, Std)) {
|
||||
// FIXME: <experimental/coroutine> should have been included already.
|
||||
// If we require it to include <new> then this diagnostic is no longer
|
||||
// needed.
|
||||
S.Diag(Loc, diag::err_implicit_coroutine_std_nothrow_type_not_found);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME: Mark the variable as ODR used. This currently does not work
|
||||
// likely due to the scope at in which this function is called.
|
||||
auto *VD = Result.getAsSingle<VarDecl>();
|
||||
if (!VD) {
|
||||
Result.suppressDiagnostics();
|
||||
// We found something weird. Complain about the first thing we found.
|
||||
NamedDecl *Found = *Result.begin();
|
||||
S.Diag(Found->getLocation(), diag::err_malformed_std_nothrow);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ExprResult DR = S.BuildDeclRefExpr(VD, VD->getType(), VK_LValue, Loc);
|
||||
if (DR.isInvalid())
|
||||
return nullptr;
|
||||
|
||||
return DR.get();
|
||||
}
|
||||
|
||||
// Find an appropriate delete for the promise.
|
||||
static FunctionDecl *findDeleteForPromise(Sema &S, SourceLocation Loc,
|
||||
QualType PromiseType) {
|
||||
|
@ -880,51 +847,23 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type))
|
||||
return false;
|
||||
|
||||
const bool RequiresNoThrowAlloc = ReturnStmtOnAllocFailure != nullptr;
|
||||
|
||||
// FIXME: Add nothrow_t placement arg for global alloc
|
||||
// if ReturnStmtOnAllocFailure != nullptr.
|
||||
// FIXME: Add support for stateful allocators.
|
||||
|
||||
FunctionDecl *OperatorNew = nullptr;
|
||||
FunctionDecl *OperatorDelete = nullptr;
|
||||
FunctionDecl *UnusedResult = nullptr;
|
||||
bool PassAlignment = false;
|
||||
MultiExprArg PlacementArgs = None;
|
||||
|
||||
S.FindAllocationFunctions(Loc, SourceRange(),
|
||||
/*UseGlobal*/ false, PromiseType,
|
||||
/*isArray*/ false, PassAlignment, PlacementArgs,
|
||||
OperatorNew, UnusedResult);
|
||||
/*isArray*/ false, PassAlignment,
|
||||
/*PlacementArgs*/ None, OperatorNew, UnusedResult);
|
||||
|
||||
bool IsGlobalOverload =
|
||||
OperatorNew && !isa<CXXRecordDecl>(OperatorNew->getDeclContext());
|
||||
// If we didn't find a class-local new declaration and non-throwing new
|
||||
// was is required then we need to lookup the non-throwing global operator
|
||||
// instead.
|
||||
if (RequiresNoThrowAlloc && (!OperatorNew || IsGlobalOverload)) {
|
||||
auto *StdNoThrow = buildStdNoThrowDeclRef(S, Loc);
|
||||
if (!StdNoThrow)
|
||||
return false;
|
||||
PlacementArgs = MultiExprArg(StdNoThrow);
|
||||
OperatorNew = nullptr;
|
||||
S.FindAllocationFunctions(Loc, SourceRange(),
|
||||
/*UseGlobal*/ true, PromiseType,
|
||||
/*isArray*/ false, PassAlignment, PlacementArgs,
|
||||
OperatorNew, UnusedResult);
|
||||
}
|
||||
OperatorDelete = findDeleteForPromise(S, Loc, PromiseType);
|
||||
|
||||
if (OperatorNew && RequiresNoThrowAlloc) {
|
||||
const auto *FT = OperatorNew->getType()->getAs<FunctionProtoType>();
|
||||
if (!FT->isNothrow(S.Context, /*ResultIfDependent*/ false)) {
|
||||
S.Diag(OperatorNew->getLocation(),
|
||||
diag::err_coroutine_promise_new_requires_nothrow)
|
||||
<< OperatorNew;
|
||||
S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
|
||||
<< OperatorNew;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((OperatorDelete = findDeleteForPromise(S, Loc, PromiseType)) == nullptr)
|
||||
if (!OperatorDelete || !OperatorNew)
|
||||
return false;
|
||||
|
||||
Expr *FramePtr =
|
||||
|
@ -940,13 +879,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
if (NewRef.isInvalid())
|
||||
return false;
|
||||
|
||||
SmallVector<Expr *, 2> NewArgs{FrameSize};
|
||||
for (auto Arg : PlacementArgs)
|
||||
NewArgs.push_back(Arg);
|
||||
|
||||
ExprResult NewExpr =
|
||||
S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc);
|
||||
NewExpr = S.ActOnFinishFullExpr(NewExpr.get());
|
||||
S.ActOnCallExpr(S.getCurScope(), NewRef.get(), Loc, FrameSize, Loc);
|
||||
if (NewExpr.isInvalid())
|
||||
return false;
|
||||
|
||||
|
@ -972,7 +906,6 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
|
||||
ExprResult DeleteExpr =
|
||||
S.ActOnCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
|
||||
DeleteExpr = S.ActOnFinishFullExpr(DeleteExpr.get());
|
||||
if (DeleteExpr.isInvalid())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -19,19 +19,8 @@ struct coroutine_handle<void> {
|
|||
coroutine_handle(coroutine_handle<PromiseType>) {}
|
||||
};
|
||||
|
||||
} // end namespace experimental
|
||||
|
||||
struct nothrow_t {};
|
||||
constexpr nothrow_t nothrow = {};
|
||||
|
||||
} // end namespace std
|
||||
|
||||
// Required when get_return_object_on_allocation_failure() is defined by
|
||||
// the promise.
|
||||
using SizeT = decltype(sizeof(int));
|
||||
void* operator new(SizeT __sz, const std::nothrow_t&) noexcept;
|
||||
void operator delete(void* __p, const std::nothrow_t&) noexcept;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct suspend_always {
|
||||
bool await_ready() { return false; }
|
||||
|
@ -156,7 +145,7 @@ struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
|
|||
extern "C" int f4(promise_on_alloc_failure_tag) {
|
||||
// CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
|
||||
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
|
||||
// CHECK: %[[MEM:.+]] = call i8* @_ZnwmRKSt9nothrow_t(i64 %[[SIZE]], %"struct.std::nothrow_t"* dereferenceable(1) @_ZStL7nothrow)
|
||||
// CHECK: %[[MEM:.+]] = call i8* @_Znwm(i64 %[[SIZE]])
|
||||
// CHECK: %[[OK:.+]] = icmp ne i8* %[[MEM]], null
|
||||
// CHECK: br i1 %[[OK]], label %[[OKBB:.+]], label %[[ERRBB:.+]]
|
||||
|
||||
|
|
|
@ -654,18 +654,6 @@ float badly_specialized_coro_handle() { // expected-error {{std::experimental::c
|
|||
co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
constexpr nothrow_t nothrow = {};
|
||||
}
|
||||
|
||||
using SizeT = decltype(sizeof(int));
|
||||
|
||||
void* operator new(SizeT __sz, const std::nothrow_t&) noexcept;
|
||||
void operator delete(void* __p, const std::nothrow_t&) noexcept;
|
||||
|
||||
|
||||
|
||||
struct promise_on_alloc_failure_tag {};
|
||||
|
||||
template<>
|
||||
|
@ -706,43 +694,3 @@ coro<T> dependent_private_alloc_failure_handler(T) {
|
|||
}
|
||||
template coro<bad_promise_11> dependent_private_alloc_failure_handler(bad_promise_11);
|
||||
// expected-note@-1 {{requested here}}
|
||||
|
||||
struct bad_promise_12 {
|
||||
coro<bad_promise_12> get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void unhandled_exception();
|
||||
void return_void();
|
||||
static coro<bad_promise_12> get_return_object_on_allocation_failure();
|
||||
|
||||
static void* operator new(SizeT);
|
||||
// expected-error@-1 2 {{'operator new' is required to have a non-throwing noexcept specification when the promise type declares 'get_return_object_on_allocation_failure()'}}
|
||||
};
|
||||
coro<bad_promise_12> throwing_in_class_new() { // expected-note {{call to 'operator new' implicitly required by coroutine function here}}
|
||||
co_return;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
coro<T> dependent_throwing_in_class_new(T) { // expected-note {{call to 'operator new' implicitly required by coroutine function here}}
|
||||
co_return;
|
||||
}
|
||||
template coro<bad_promise_12> dependent_throwing_in_class_new(bad_promise_12); // expected-note {{requested here}}
|
||||
|
||||
|
||||
struct good_promise_13 {
|
||||
coro<good_promise_13> get_return_object();
|
||||
suspend_always initial_suspend();
|
||||
suspend_always final_suspend();
|
||||
void unhandled_exception();
|
||||
void return_void();
|
||||
static coro<good_promise_13> get_return_object_on_allocation_failure();
|
||||
};
|
||||
coro<good_promise_13> uses_nothrow_new() {
|
||||
co_return;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
coro<T> dependent_uses_nothrow_new(T) {
|
||||
co_return;
|
||||
}
|
||||
template coro<good_promise_13> dependent_uses_nothrow_new(good_promise_13);
|
||||
|
|
Loading…
Reference in New Issue