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 @@ -76,9 +76,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/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10167,11 +10167,11 @@ Ref_Compatible }; - ReferenceCompareResult CompareReferenceRelationship(SourceLocation Loc, - QualType T1, QualType T2, - bool &DerivedToBase, - bool &ObjCConversion, - bool &ObjCLifetimeConversion); + ReferenceCompareResult + CompareReferenceRelationship(SourceLocation Loc, QualType T1, QualType T2, + bool &DerivedToBase, bool &FuncRefConversion, + bool &ObjCConversion, + bool &ObjCLifetimeConversion); ExprResult checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, Expr *CastExpr, CastKind &CastKind, diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1302,6 +1302,7 @@ // this is the only cast possibility, so we issue an error if we fail now. // FIXME: Should allow casting away constness if CStyle. bool DerivedToBase; + bool FuncRefConversion; bool ObjCConversion; bool ObjCLifetimeConversion; QualType FromType = SrcExpr->getType(); @@ -1312,8 +1313,8 @@ } Sema::ReferenceCompareResult RefResult = Self.CompareReferenceRelationship( - SrcExpr->getBeginLoc(), ToType, FromType, DerivedToBase, ObjCConversion, - ObjCLifetimeConversion); + SrcExpr->getBeginLoc(), ToType, FromType, DerivedToBase, + FuncRefConversion, ObjCConversion, ObjCLifetimeConversion); if (RefResult != Sema::Ref_Compatible) { if (CStyle || RefResult == Sema::Ref_Incompatible) return TC_NotApplicable; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5835,9 +5835,10 @@ LVK == RVK && LVK != VK_RValue) { // DerivedToBase was already handled by the class-specific case above. // FIXME: Should we allow ObjC conversions here? - bool DerivedToBase, ObjCConversion, ObjCLifetimeConversion; + bool DerivedToBase, FuncRefConversion, ObjCConversion, + ObjCLifetimeConversion; if (CompareReferenceRelationship( - QuestionLoc, LTy, RTy, DerivedToBase, + QuestionLoc, LTy, RTy, DerivedToBase, FuncRefConversion, ObjCConversion, ObjCLifetimeConversion) == Ref_Compatible && !DerivedToBase && !ObjCConversion && !ObjCLifetimeConversion && // [...] subject to the constraint that the reference must bind @@ -5847,7 +5848,7 @@ RHS = ImpCastExprToType(RHS.get(), LTy, CK_NoOp, RVK); RTy = RHS.get()->getType(); } else if (CompareReferenceRelationship( - QuestionLoc, RTy, LTy, DerivedToBase, + QuestionLoc, RTy, LTy, DerivedToBase, FuncRefConversion, ObjCConversion, ObjCLifetimeConversion) == Ref_Compatible && !DerivedToBase && !ObjCConversion && !ObjCLifetimeConversion && !LHS.get()->refersToBitField() && 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; @@ -4228,10 +4236,10 @@ return; SourceLocation DeclLoc = Initializer->getBeginLoc(); - bool dummy1, dummy2, dummy3; - Sema::ReferenceCompareResult RefRelationship - = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, dummy1, - dummy2, dummy3); + bool dummy1, dummy2, dummy3, dummy4; + Sema::ReferenceCompareResult RefRelationship = + S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, dummy1, dummy2, + dummy3, dummy4); if (RefRelationship >= Sema::Ref_Related) { // Try to bind the reference here. TryReferenceInitializationCore(S, Entity, Kind, Initializer, cv1T1, T1, @@ -4469,13 +4477,15 @@ QualType T2 = cv2T2.getUnqualifiedType(); bool DerivedToBase; + bool FuncRefConversion; bool ObjCConversion; bool ObjCLifetimeConversion; - assert(!S.CompareReferenceRelationship(Initializer->getBeginLoc(), T1, T2, - DerivedToBase, ObjCConversion, - ObjCLifetimeConversion) && + assert(!S.CompareReferenceRelationship( + Initializer->getBeginLoc(), T1, T2, DerivedToBase, + FuncRefConversion, ObjCConversion, ObjCLifetimeConversion) && "Must have incompatible references when binding via conversion"); (void)DerivedToBase; + (void)FuncRefConversion; (void)ObjCConversion; (void)ObjCLifetimeConversion; @@ -4602,11 +4612,12 @@ // Determine whether we'll need to perform derived-to-base adjustments or // other conversions. bool NewDerivedToBase = false; + bool NewFuncRefConversion = false; bool NewObjCConversion = false; bool NewObjCLifetimeConversion = false; - Sema::ReferenceCompareResult NewRefRelationship - = S.CompareReferenceRelationship(DeclLoc, T1, cv3T3, - NewDerivedToBase, NewObjCConversion, + Sema::ReferenceCompareResult NewRefRelationship = + S.CompareReferenceRelationship(DeclLoc, T1, cv3T3, NewDerivedToBase, + NewFuncRefConversion, NewObjCConversion, NewObjCLifetimeConversion); // Add the final conversion sequence, if necessary. @@ -4639,6 +4650,8 @@ if (NewDerivedToBase) Sequence.AddDerivedToBaseCastStep(cv1T1, VK); + else if (NewFuncRefConversion) + Sequence.AddFunctionReferenceConversionStep(cv1T1); else if (NewObjCConversion) Sequence.AddObjCObjectConversionStep(cv1T1); @@ -4698,12 +4711,13 @@ bool isLValueRef = DestType->isLValueReferenceType(); bool isRValueRef = !isLValueRef; bool DerivedToBase = false; + bool FuncRefConversion = false; bool ObjCConversion = false; bool ObjCLifetimeConversion = false; Expr::Classification InitCategory = Initializer->Classify(S.Context); - Sema::ReferenceCompareResult RefRelationship - = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase, - ObjCConversion, ObjCLifetimeConversion); + Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship( + DeclLoc, cv1T1, cv2T2, DerivedToBase, FuncRefConversion, ObjCConversion, + ObjCLifetimeConversion); // C++0x [dcl.init.ref]p5: // A reference to type "cv1 T1" is initialized by an expression of type @@ -4732,6 +4746,8 @@ Initializer->getValueKind()); if (DerivedToBase) Sequence.AddDerivedToBaseCastStep(cv1T1, VK_LValue); + else if (FuncRefConversion) + Sequence.AddFunctionReferenceConversionStep(cv1T1); else if (ObjCConversion) Sequence.AddObjCObjectConversionStep(cv1T1); @@ -7863,6 +7879,7 @@ case SK_QualificationConversionLValue: case SK_QualificationConversionXValue: case SK_QualificationConversionRValue: + case SK_FunctionReferenceConversion: case SK_AtomicConversion: case SK_ConversionSequence: case SK_ConversionSequenceNoNarrowing: @@ -8128,6 +8145,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, @@ -9357,6 +9381,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/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -4296,12 +4296,10 @@ /// reference (C++ [dcl.ref.init]p4). Neither type can be a reference /// type, and the first type (T1) is the pointee type of the reference /// type being initialized. -Sema::ReferenceCompareResult -Sema::CompareReferenceRelationship(SourceLocation Loc, - QualType OrigT1, QualType OrigT2, - bool &DerivedToBase, - bool &ObjCConversion, - bool &ObjCLifetimeConversion) { +Sema::ReferenceCompareResult Sema::CompareReferenceRelationship( + SourceLocation Loc, QualType OrigT1, QualType OrigT2, bool &DerivedToBase, + bool &FuncRefConversion, bool &ObjCConversion, + bool &ObjCLifetimeConversion) { assert(!OrigT1->isReferenceType() && "T1 must be the pointee type of the reference type"); assert(!OrigT2->isReferenceType() && "T2 cannot be a reference type"); @@ -4317,6 +4315,7 @@ // reference-related to "cv2 T2" if T1 is the same type as T2, or // T1 is a base class of T2. DerivedToBase = false; + FuncRefConversion = false; ObjCConversion = false; ObjCLifetimeConversion = false; QualType ConvertedT2; @@ -4331,15 +4330,16 @@ Context.canBindObjCObjectType(UnqualT1, UnqualT2)) ObjCConversion = true; else if (UnqualT2->isFunctionType() && - IsFunctionConversion(UnqualT2, UnqualT1, ConvertedT2)) + IsFunctionConversion(UnqualT2, UnqualT1, ConvertedT2)) { // C++1z [dcl.init.ref]p4: // cv1 T1" is reference-compatible with "cv2 T2" if [...] T2 is "noexcept // function" and T1 is "function" // // We extend this to also apply to 'noreturn', so allow any function // conversion between function types. + FuncRefConversion = true; return Ref_Compatible; - else + } else return Ref_Incompatible; // At this point, we know that T1 and T2 are reference-related (at @@ -4418,6 +4418,7 @@ if (AllowRvalues) { bool DerivedToBase = false; + bool FuncPtrConversion = false; bool ObjCConversion = false; bool ObjCLifetimeConversion = false; @@ -4432,12 +4433,13 @@ if (!ConvTemplate && S.CompareReferenceRelationship( - DeclLoc, - Conv->getConversionType().getNonReferenceType() - .getUnqualifiedType(), - DeclType.getNonReferenceType().getUnqualifiedType(), - DerivedToBase, ObjCConversion, ObjCLifetimeConversion) == - Sema::Ref_Incompatible) + DeclLoc, + Conv->getConversionType() + .getNonReferenceType() + .getUnqualifiedType(), + DeclType.getNonReferenceType().getUnqualifiedType(), + DerivedToBase, FuncPtrConversion, ObjCConversion, + ObjCLifetimeConversion) == Sema::Ref_Incompatible) continue; } else { // If the conversion function doesn't return a reference type, @@ -4539,13 +4541,13 @@ // Compute some basic properties of the types and the initializer. bool isRValRef = DeclType->isRValueReferenceType(); bool DerivedToBase = false; + bool FuncPtrConversion = false; bool ObjCConversion = false; bool ObjCLifetimeConversion = false; Expr::Classification InitCategory = Init->Classify(S.Context); - Sema::ReferenceCompareResult RefRelationship - = S.CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase, - ObjCConversion, ObjCLifetimeConversion); - + Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship( + DeclLoc, T1, T2, DerivedToBase, FuncPtrConversion, ObjCConversion, + ObjCLifetimeConversion); // C++0x [dcl.init.ref]p5: // A reference to type "cv1 T1" is initialized by an expression @@ -4970,9 +4972,10 @@ bool dummy1 = false; bool dummy2 = false; bool dummy3 = false; + bool dummy4 = false; Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship(From->getBeginLoc(), T1, T2, dummy1, - dummy2, dummy3); + dummy2, dummy3, dummy4); if (RefRelationship >= Sema::Ref_Related) { return TryReferenceInit(S, Init, ToType, /*FIXME*/ From->getBeginLoc(), 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;