diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -77,9 +77,10 @@ CAST_OPERATION(LValueToRValue) /// CK_NoOp - A conversion which does not affect the type other than -/// (possibly) adding qualifiers. +/// (possibly) adding qualifiers or removing noexcept. /// int -> int /// char** -> const char * const * +/// void () noexcept -> void () CAST_OPERATION(NoOp) /// CK_BaseToDerived - A conversion from a C++ class pointer/reference diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -840,6 +840,9 @@ /// Perform a qualification conversion, producing an lvalue. SK_QualificationConversionLValue, + /// Perform a function reference conversion, see [dcl.init.ref]p4. + SK_FunctionReferenceConversion, + /// Perform a conversion adding _Atomic to a type. SK_AtomicConversion, @@ -1288,6 +1291,10 @@ void AddQualificationConversionStep(QualType Ty, ExprValueKind Category); + /// Add a new step that performs a function reference conversion to the + /// given type. + void AddFunctionReferenceConversionStep(QualType Ty); + /// Add a new step that performs conversion from non-atomic to atomic /// type. void AddAtomicConversionStep(QualType Ty); 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 @@ -3442,6 +3442,7 @@ case SK_QualificationConversionRValue: case SK_QualificationConversionXValue: case SK_QualificationConversionLValue: + case SK_FunctionReferenceConversion: case SK_AtomicConversion: case SK_ListInitialization: case SK_UnwrapInitList: @@ -3620,6 +3621,13 @@ Steps.push_back(S); } +void InitializationSequence::AddFunctionReferenceConversionStep(QualType Ty) { + Step S; + S.Kind = SK_FunctionReferenceConversion; + S.Type = Ty; + Steps.push_back(S); +} + void InitializationSequence::AddAtomicConversionStep(QualType Ty) { Step S; S.Kind = SK_AtomicConversion; @@ -4653,7 +4661,7 @@ else if (RefConv & Sema::ReferenceConversions::ObjC) Sequence.AddObjCObjectConversionStep(cv1T1); else if (RefConv & Sema::ReferenceConversions::Function) - Sequence.AddQualificationConversionStep(cv1T1, VK); + Sequence.AddFunctionReferenceConversionStep(cv1T1); else if (RefConv & Sema::ReferenceConversions::Qualification) { if (!S.Context.hasSameType(cv1T4, cv1T1)) Sequence.AddQualificationConversionStep(cv1T1, VK); @@ -4755,12 +4763,12 @@ Sequence.AddDerivedToBaseCastStep(cv1T1, VK_LValue); else Sequence.AddObjCObjectConversionStep(cv1T1); - } else if (RefConv & (Sema::ReferenceConversions::Qualification | - Sema::ReferenceConversions::Function)) { + } else if (RefConv & Sema::ReferenceConversions::Qualification) { // Perform a (possibly multi-level) qualification conversion. - // FIXME: Should we use a different step kind for function conversions? Sequence.AddQualificationConversionStep(cv1T1, Initializer->getValueKind()); + } else if (RefConv & Sema::ReferenceConversions::Function) { + Sequence.AddFunctionReferenceConversionStep(cv1T1); } // We only create a temporary here when binding a reference to a @@ -8038,6 +8046,7 @@ case SK_QualificationConversionLValue: case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: + case SK_FunctionReferenceConversion: case SK_AtomicConversion: case SK_ConversionSequence: case SK_ConversionSequenceNoNarrowing: @@ -8303,6 +8312,13 @@ break; } + case SK_FunctionReferenceConversion: + assert(CurInit.get()->isLValue() && + "function reference should be lvalue"); + CurInit = + S.ImpCastExprToType(CurInit.get(), Step->Type, CK_NoOp, VK_LValue); + break; + case SK_AtomicConversion: { assert(CurInit.get()->isRValue() && "cannot convert glvalue to atomic"); CurInit = S.ImpCastExprToType(CurInit.get(), Step->Type, @@ -9563,6 +9579,10 @@ OS << "qualification conversion (lvalue)"; break; + case SK_FunctionReferenceConversion: + OS << "function reference conversion"; + break; + case SK_AtomicConversion: OS << "non-atomic-to-atomic conversion"; break; diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++17 -ast-dump %s | FileCheck %s + +void f() noexcept; + +// CHECK: VarDecl {{.*}} ref 'void (&)()' cinit +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()':'void ()' lvalue +// CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' lvalue Function {{.*}} 'f' 'void () noexcept' +void (&ref)() = f; + +struct X { + typedef void (&ref)() noexcept; + operator ref(); +} x; + +// CHECK: VarDecl {{.*}} xp 'void (&)()' cinit +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()':'void ()' lvalue +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void () noexcept':'void () noexcept' lvalue +void (&xp)() = x; diff --git a/clang/test/CodeGenCXX/implicit-function-conversion.cpp b/clang/test/CodeGenCXX/implicit-function-conversion.cpp deleted file mode 100644 --- a/clang/test/CodeGenCXX/implicit-function-conversion.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-unknown-linux -std=c++17 | FileCheck %s - -double a(double) noexcept; -int b(double (&)(double)); - -// CHECK: call i32 @_Z1bRFddE(double (double)* nonnull @_Z1ad) -int c = b(a);