PR46640: Permit the first parameter of a destroying 'operator delete' to

be dependent if it names the right type.

This matches the GCC behavior, but no longer matches the standard
wording. However, the standard wording in this case is not in line with
the intent, which was to require the enclosing class type to be named
directly. I've reported this wording oversight to the committee.
This commit is contained in:
Richard Smith 2020-07-08 14:26:18 -07:00
parent 74a148ad39
commit 903bda14c3
2 changed files with 39 additions and 19 deletions

View File

@ -15216,12 +15216,6 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
QualType ResultType =
FnDecl->getType()->castAs<FunctionType>()->getReturnType();
// Check that the result type is not dependent.
if (ResultType->isDependentType())
return SemaRef.Diag(FnDecl->getLocation(),
diag::err_operator_new_delete_dependent_result_type)
<< FnDecl->getDeclName() << ExpectedResultType;
// The operator is valid on any address space for OpenCL.
if (SemaRef.getLangOpts().OpenCLCPlusPlus) {
if (auto *PtrTy = ResultType->getAs<PointerType>()) {
@ -15230,10 +15224,16 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
}
// Check that the result type is what we expect.
if (SemaRef.Context.getCanonicalType(ResultType) != ExpectedResultType)
return SemaRef.Diag(FnDecl->getLocation(),
diag::err_operator_new_delete_invalid_result_type)
<< FnDecl->getDeclName() << ExpectedResultType;
if (SemaRef.Context.getCanonicalType(ResultType) != ExpectedResultType) {
// Reject even if the type is dependent; an operator delete function is
// required to have a non-dependent result type.
return SemaRef.Diag(
FnDecl->getLocation(),
ResultType->isDependentType()
? diag::err_operator_new_delete_dependent_result_type
: diag::err_operator_new_delete_invalid_result_type)
<< FnDecl->getDeclName() << ExpectedResultType;
}
// A function template must have at least 2 parameters.
if (FnDecl->getDescribedFunctionTemplate() && FnDecl->getNumParams() < 2)
@ -15247,13 +15247,7 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
diag::err_operator_new_delete_too_few_parameters)
<< FnDecl->getDeclName();
// Check the first parameter type is not dependent.
QualType FirstParamType = FnDecl->getParamDecl(0)->getType();
if (FirstParamType->isDependentType())
return SemaRef.Diag(FnDecl->getLocation(), DependentParamTypeDiag)
<< FnDecl->getDeclName() << ExpectedFirstParamType;
// Check that the first parameter type is what we expect.
if (SemaRef.getLangOpts().OpenCLCPlusPlus) {
// The operator is valid on any address space for OpenCL.
if (auto *PtrTy =
@ -15261,10 +15255,18 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl,
FirstParamType = RemoveAddressSpaceFromPtr(SemaRef, PtrTy);
}
}
// Check that the first parameter type is what we expect.
if (SemaRef.Context.getCanonicalType(FirstParamType).getUnqualifiedType() !=
ExpectedFirstParamType)
return SemaRef.Diag(FnDecl->getLocation(), InvalidParamTypeDiag)
<< FnDecl->getDeclName() << ExpectedFirstParamType;
ExpectedFirstParamType) {
// The first parameter type is not allowed to be dependent. As a tentative
// DR resolution, we allow a dependent parameter type if it is the right
// type anyway, to allow destroying operator delete in class templates.
return SemaRef.Diag(FnDecl->getLocation(), FirstParamType->isDependentType()
? DependentParamTypeDiag
: InvalidParamTypeDiag)
<< FnDecl->getDeclName() << ExpectedFirstParamType;
}
return false;
}

View File

@ -123,3 +123,21 @@ namespace first_param_conversion {
delete e; // expected-error {{ambiguous conversion from derived class 'first_param_conversion::E' to base class 'first_param_conversion::B':}}
}
}
namespace templated {
template<typename T> using id_alias = T;
template<typename T> struct id_struct { using type = T; };
template<typename T> struct A {
void operator delete(A *, std::destroying_delete_t);
};
template<typename T> struct B {
void operator delete(B<T> *, std::destroying_delete_t);
};
template<typename T> struct C {
void operator delete(id_alias<C> *, std::destroying_delete_t);
};
template<typename T> struct D {
void operator delete(typename id_struct<D>::type *, std::destroying_delete_t); // expected-error {{use 'D<T> *'}}
};
}