diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index eb001a77518b..9cad6debc600 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -15216,12 +15216,6 @@ CheckOperatorNewDeleteTypes(Sema &SemaRef, const FunctionDecl *FnDecl, QualType ResultType = FnDecl->getType()->castAs()->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()) { @@ -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; } diff --git a/clang/test/SemaCXX/cxx2a-destroying-delete.cpp b/clang/test/SemaCXX/cxx2a-destroying-delete.cpp index 553b7a7080fe..015d11e65526 100644 --- a/clang/test/SemaCXX/cxx2a-destroying-delete.cpp +++ b/clang/test/SemaCXX/cxx2a-destroying-delete.cpp @@ -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 using id_alias = T; + template struct id_struct { using type = T; }; + + template struct A { + void operator delete(A *, std::destroying_delete_t); + }; + template struct B { + void operator delete(B *, std::destroying_delete_t); + }; + template struct C { + void operator delete(id_alias *, std::destroying_delete_t); + }; + template struct D { + void operator delete(typename id_struct::type *, std::destroying_delete_t); // expected-error {{use 'D *'}} + }; +}