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 @@ -818,6 +818,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, @@ -1263,6 +1266,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 @@ -3417,6 +3417,7 @@ case SK_QualificationConversionRValue: case SK_QualificationConversionXValue: case SK_QualificationConversionLValue: + case SK_FunctionReferenceConversion: case SK_AtomicConversion: case SK_ListInitialization: case SK_UnwrapInitList: @@ -3594,6 +3595,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; @@ -4627,7 +4635,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); @@ -4725,12 +4733,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 @@ -7897,6 +7905,7 @@ case SK_QualificationConversionLValue: case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: + case SK_FunctionReferenceConversion: case SK_AtomicConversion: case SK_ConversionSequence: case SK_ConversionSequenceNoNarrowing: @@ -8162,6 +8171,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, @@ -9398,6 +9414,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,17 @@ +// RUN: %clang_cc1 -std=c++17 -ast-dump %s | FileCheck %s + +void f() noexcept; +// CHECK: VarDecl {{.*}} fp 'void (&)()' cinit +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()':'void ()' lvalue +// CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' lvalue Function {{.*}} 'f' 'void () noexcept' +void (&fp)() = f; + +struct X { + typedef void (&fp)() noexcept; + operator fp(); +} x; + +// CHECK: VarDecl {{.*}} xp 'void (&)()' cinit +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()':'void ()' lvalue +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void () noexcept':'void () noexcept' lvalue +void (&xp)() = x;