summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Sema/Overload.h3
-rw-r--r--clang/include/clang/Sema/Sema.h10
-rw-r--r--clang/lib/Sema/SemaExprCXX.cpp193
-rw-r--r--clang/lib/Sema/SemaOverload.cpp54
-rw-r--r--clang/test/CXX/expr/p13.cpp51
5 files changed, 255 insertions, 56 deletions
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 83c6554c64d..f677afe8e26 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -145,7 +145,8 @@ namespace clang {
/// pointer-to-member conversion, or boolean conversion.
ImplicitConversionKind Second : 8;
- /// Third - The third conversion can be a qualification conversion.
+ /// Third - The third conversion can be a qualification conversion
+ /// or a function conversion.
ImplicitConversionKind Third : 8;
/// \brief Whether this is the deprecated conversion of a
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1dd415d3497..08e6a29ca11 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8954,13 +8954,15 @@ public:
ExprResult &cond, ExprResult &lhs, ExprResult &rhs,
ExprValueKind &VK, ExprObjectKind &OK, SourceLocation questionLoc);
QualType FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2,
- bool *NonStandardCompositeType = nullptr);
+ bool *NonStandardCompositeType = nullptr,
+ bool ConvertArgs = true);
QualType FindCompositePointerType(SourceLocation Loc,
ExprResult &E1, ExprResult &E2,
- bool *NonStandardCompositeType = nullptr) {
+ bool *NonStandardCompositeType = nullptr,
+ bool ConvertArgs = true) {
Expr *E1Tmp = E1.get(), *E2Tmp = E2.get();
- QualType Composite = FindCompositePointerType(Loc, E1Tmp, E2Tmp,
- NonStandardCompositeType);
+ QualType Composite = FindCompositePointerType(
+ Loc, E1Tmp, E2Tmp, NonStandardCompositeType, ConvertArgs);
E1 = E1Tmp;
E2 = E2Tmp;
return Composite;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b660bd34865..83474cb7f83 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3610,16 +3610,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
// Nothing else to do.
break;
- case ICK_Function_Conversion:
- // If both sides are functions (or pointers/references to them), there could
- // be incompatible exception declarations.
- if (CheckExceptionSpecCompatibility(From, ToType))
- return ExprError();
-
- From = ImpCastExprToType(From, ToType, CK_NoOp,
- VK_RValue, /*BasePath=*/nullptr, CCK).get();
- break;
-
case ICK_Integral_Promotion:
case ICK_Integral_Conversion:
if (ToType->isBooleanType()) {
@@ -3866,6 +3856,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
case ICK_Lvalue_To_Rvalue:
case ICK_Array_To_Pointer:
case ICK_Function_To_Pointer:
+ case ICK_Function_Conversion:
case ICK_Qualification:
case ICK_Num_Conversion_Kinds:
case ICK_C_Only_Conversion:
@@ -3878,6 +3869,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
// Nothing to do.
break;
+ case ICK_Function_Conversion:
+ // If both sides are functions (or pointers/references to them), there could
+ // be incompatible exception declarations.
+ if (CheckExceptionSpecCompatibility(From, ToType))
+ return ExprError();
+
+ From = ImpCastExprToType(From, ToType, CK_NoOp,
+ VK_RValue, /*BasePath=*/nullptr, CCK).get();
+ break;
+
case ICK_Qualification: {
// The qualification keeps the category of the inner expression, unless the
// target type isn't a reference.
@@ -5393,6 +5394,20 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
if (LHS.get()->getObjectKind() == OK_BitField ||
RHS.get()->getObjectKind() == OK_BitField)
OK = OK_BitField;
+
+ // If we have function pointer types, unify them anyway to unify their
+ // exception specifications, if any.
+ if (LTy->isFunctionPointerType() || LTy->isMemberFunctionPointerType()) {
+ Qualifiers Qs = LTy.getQualifiers();
+ LTy = FindCompositePointerType(QuestionLoc, LHS, RHS, nullptr,
+ /*ConvertArgs*/false);
+ LTy = Context.getQualifiedType(LTy, Qs);
+
+ assert(!LTy.isNull() && "failed to find composite pointer type for "
+ "canonically equivalent function ptr types");
+ assert(Context.hasSameType(LTy, RTy) && "bad composite pointer type");
+ }
+
return LTy;
}
@@ -5447,6 +5462,14 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
RHS = RHSCopy;
}
+ // If we have function pointer types, unify them anyway to unify their
+ // exception specifications, if any.
+ if (LTy->isFunctionPointerType() || LTy->isMemberFunctionPointerType()) {
+ LTy = FindCompositePointerType(QuestionLoc, LHS, RHS);
+ assert(!LTy.isNull() && "failed to find composite pointer type for "
+ "canonically equivalent function ptr types");
+ }
+
return LTy;
}
@@ -5517,6 +5540,78 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
return QualType();
}
+static FunctionProtoType::ExceptionSpecInfo
+mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
+ FunctionProtoType::ExceptionSpecInfo ESI2,
+ SmallVectorImpl<QualType> &ExceptionTypeStorage) {
+ ExceptionSpecificationType EST1 = ESI1.Type;
+ ExceptionSpecificationType EST2 = ESI2.Type;
+
+ // If either of them can throw anything, that is the result.
+ if (EST1 == EST_None) return ESI1;
+ if (EST2 == EST_None) return ESI2;
+ if (EST1 == EST_MSAny) return ESI1;
+ if (EST2 == EST_MSAny) return ESI2;
+
+ // If either of them is non-throwing, the result is the other.
+ if (EST1 == EST_DynamicNone) return ESI2;
+ if (EST2 == EST_DynamicNone) return ESI1;
+ if (EST1 == EST_BasicNoexcept) return ESI2;
+ if (EST2 == EST_BasicNoexcept) return ESI1;
+
+ // If either of them is a non-value-dependent computed noexcept, that
+ // determines the result.
+ if (EST2 == EST_ComputedNoexcept && ESI2.NoexceptExpr &&
+ !ESI2.NoexceptExpr->isValueDependent())
+ return !ESI2.NoexceptExpr->EvaluateKnownConstInt(S.Context) ? ESI2 : ESI1;
+ if (EST1 == EST_ComputedNoexcept && ESI1.NoexceptExpr &&
+ !ESI1.NoexceptExpr->isValueDependent())
+ return !ESI1.NoexceptExpr->EvaluateKnownConstInt(S.Context) ? ESI1 : ESI2;
+ // If we're left with value-dependent computed noexcept expressions, we're
+ // stuck. Before C++17, we can just drop the exception specification entirely,
+ // since it's not actually part of the canonical type. And this should never
+ // happen in C++17, because it would mean we were computing the composite
+ // pointer type of dependent types, which should never happen.
+ if (EST1 == EST_ComputedNoexcept || EST2 == EST_ComputedNoexcept) {
+ assert(!S.getLangOpts().CPlusPlus1z &&
+ "computing composite pointer type of dependent types");
+ return FunctionProtoType::ExceptionSpecInfo();
+ }
+
+ // Switch over the possibilities so that people adding new values know to
+ // update this function.
+ switch (EST1) {
+ case EST_None:
+ case EST_DynamicNone:
+ case EST_MSAny:
+ case EST_BasicNoexcept:
+ case EST_ComputedNoexcept:
+ llvm_unreachable("handled above");
+
+ case EST_Dynamic: {
+ // This is the fun case: both exception specifications are dynamic. Form
+ // the union of the two lists.
+ assert(EST2 == EST_Dynamic && "other cases should already be handled");
+ llvm::SmallPtrSet<QualType, 8> Found;
+ for (auto &Exceptions : {ESI1.Exceptions, ESI2.Exceptions})
+ for (QualType E : Exceptions)
+ if (Found.insert(S.Context.getCanonicalType(E)).second)
+ ExceptionTypeStorage.push_back(E);
+
+ FunctionProtoType::ExceptionSpecInfo Result(EST_Dynamic);
+ Result.Exceptions = ExceptionTypeStorage;
+ return Result;
+ }
+
+ case EST_Unevaluated:
+ case EST_Uninstantiated:
+ case EST_Unparsed:
+ llvm_unreachable("shouldn't see unresolved exception specifications here");
+ }
+
+ llvm_unreachable("invalid ExceptionSpecificationType");
+}
+
/// \brief Find a merged pointer type and convert the two expressions to it.
///
/// This finds the composite pointer type (or member pointer type) for @p E1
@@ -5531,9 +5626,12 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
/// a non-standard (but still sane) composite type to which both expressions
/// can be converted. When such a type is chosen, \c *NonStandardCompositeType
/// will be set true.
+///
+/// \param ConvertArgs If \c false, do not convert E1 and E2 to the target type.
QualType Sema::FindCompositePointerType(SourceLocation Loc,
Expr *&E1, Expr *&E2,
- bool *NonStandardCompositeType) {
+ bool *NonStandardCompositeType,
+ bool ConvertArgs) {
if (NonStandardCompositeType)
*NonStandardCompositeType = false;
@@ -5560,16 +5658,18 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
// - if either p1 or p2 is a null pointer constant, T2 or T1, respectively;
if (T1IsPointerLike &&
E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
- E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType()
- ? CK_NullToMemberPointer
- : CK_NullToPointer).get();
+ if (ConvertArgs)
+ E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType()
+ ? CK_NullToMemberPointer
+ : CK_NullToPointer).get();
return T1;
}
if (T2IsPointerLike &&
E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
- E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType()
- ? CK_NullToMemberPointer
- : CK_NullToPointer).get();
+ if (ConvertArgs)
+ E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType()
+ ? CK_NullToMemberPointer
+ : CK_NullToPointer).get();
return T2;
}
@@ -5615,8 +5715,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
// exists.
SmallVector<unsigned, 4> QualifierUnion;
SmallVector<std::pair<const Type *, const Type *>, 4> MemberOfClass;
- QualType Composite1 = Context.getCanonicalType(T1);
- QualType Composite2 = Context.getCanonicalType(T2);
+ QualType Composite1 = T1;
+ QualType Composite2 = T2;
unsigned NeedConstBefore = 0;
while (true) {
const PointerType *Ptr1, *Ptr2;
@@ -5662,6 +5762,41 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
break;
}
+ // Apply the function pointer conversion to unify the types. We've already
+ // unwrapped down to the function types, and we want to merge rather than
+ // just convert, so do this ourselves rather than calling
+ // IsFunctionConversion.
+ //
+ // FIXME: In order to match the standard wording as closely as possible, we
+ // currently only do this under a single level of pointers. Ideally, we would
+ // allow this in general, and set NeedConstBefore to the relevant depth on
+ // the side(s) where we changed anything.
+ if (QualifierUnion.size() == 1) {
+ if (auto *FPT1 = Composite1->getAs<FunctionProtoType>()) {
+ if (auto *FPT2 = Composite2->getAs<FunctionProtoType>()) {
+ FunctionProtoType::ExtProtoInfo EPI1 = FPT1->getExtProtoInfo();
+ FunctionProtoType::ExtProtoInfo EPI2 = FPT2->getExtProtoInfo();
+
+ // The result is noreturn if both operands are.
+ bool Noreturn =
+ EPI1.ExtInfo.getNoReturn() && EPI2.ExtInfo.getNoReturn();
+ EPI1.ExtInfo = EPI1.ExtInfo.withNoReturn(Noreturn);
+ EPI2.ExtInfo = EPI2.ExtInfo.withNoReturn(Noreturn);
+
+ // The result is nothrow if both operands are.
+ SmallVector<QualType, 8> ExceptionTypeStorage;
+ EPI1.ExceptionSpec = EPI2.ExceptionSpec =
+ mergeExceptionSpecs(*this, EPI1.ExceptionSpec, EPI2.ExceptionSpec,
+ ExceptionTypeStorage);
+
+ Composite1 = Context.getFunctionType(FPT1->getReturnType(),
+ FPT1->getParamTypes(), EPI1);
+ Composite2 = Context.getFunctionType(FPT2->getReturnType(),
+ FPT2->getParamTypes(), EPI2);
+ }
+ }
+ }
+
if (NeedConstBefore && NonStandardCompositeType) {
// Extension: Add 'const' to qualifiers that come before the first qualifier
// mismatch, so that our (non-standard!) composite type meets the
@@ -5711,25 +5846,28 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
E1ToC(S, Entity, Kind, E1), E2ToC(S, Entity, Kind, E2),
Viable(E1ToC && E2ToC) {}
- QualType perform() {
+ bool perform() {
ExprResult E1Result = E1ToC.Perform(S, Entity, Kind, E1);
if (E1Result.isInvalid())
- return QualType();
+ return true;
E1 = E1Result.getAs<Expr>();
ExprResult E2Result = E2ToC.Perform(S, Entity, Kind, E2);
if (E2Result.isInvalid())
- return QualType();
+ return true;
E2 = E2Result.getAs<Expr>();
- return Composite;
+ return false;
}
};
// Try to convert to each composite pointer type.
Conversion C1(*this, Loc, E1, E2, Composite1);
- if (C1.Viable && Context.hasSameType(Composite1, Composite2))
- return C1.perform();
+ if (C1.Viable && Context.hasSameType(Composite1, Composite2)) {
+ if (ConvertArgs && C1.perform())
+ return QualType();
+ return C1.Composite;
+ }
Conversion C2(*this, Loc, E1, E2, Composite2);
if (C1.Viable == C2.Viable) {
@@ -5740,7 +5878,10 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
}
// Convert to the chosen type.
- return (C1.Viable ? C1 : C2).perform();
+ if (ConvertArgs && (C1.Viable ? C1 : C2).perform())
+ return QualType();
+
+ return C1.Viable ? C1.Composite : C2.Composite;
}
ExprResult Sema::MaybeBindToTemporary(Expr *E) {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index db97f3d57f5..8cc592e21e5 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1405,6 +1405,7 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
// - a pointer
// - a member pointer
// - a block pointer
+ // Changes here need matching changes in FindCompositePointerType.
CanQualType CanTo = Context.getCanonicalType(ToType);
CanQualType CanFrom = Context.getCanonicalType(FromType);
Type::TypeClass TyClass = CanTo->getTypeClass();
@@ -1417,8 +1418,13 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
CanTo = CanTo.getAs<BlockPointerType>()->getPointeeType();
CanFrom = CanFrom.getAs<BlockPointerType>()->getPointeeType();
} else if (TyClass == Type::MemberPointer) {
- CanTo = CanTo.getAs<MemberPointerType>()->getPointeeType();
- CanFrom = CanFrom.getAs<MemberPointerType>()->getPointeeType();
+ auto ToMPT = CanTo.getAs<MemberPointerType>();
+ auto FromMPT = CanFrom.getAs<MemberPointerType>();
+ // A function pointer conversion cannot change the class of the function.
+ if (ToMPT->getClass() != FromMPT->getClass())
+ return false;
+ CanTo = ToMPT->getPointeeType();
+ CanFrom = FromMPT->getPointeeType();
} else {
return false;
}
@@ -1757,10 +1763,6 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
// Compatible conversions (Clang extension for C function overloading)
SCS.Second = ICK_Compatible_Conversion;
FromType = ToType.getUnqualifiedType();
- } else if (S.IsFunctionConversion(FromType, ToType, FromType)) {
- // Function pointer conversions (removing 'noexcept') including removal of
- // 'noreturn' (Clang extension).
- SCS.Second = ICK_Function_Conversion;
} else if (IsTransparentUnionStandardConversion(S, From, ToType,
InOverloadResolution,
SCS, CStyle)) {
@@ -1782,34 +1784,36 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
}
SCS.setToType(1, FromType);
- QualType CanonFrom;
- QualType CanonTo;
- // The third conversion can be a qualification conversion (C++ 4p1).
+ // The third conversion can be a function pointer conversion or a
+ // qualification conversion (C++ [conv.fctptr], [conv.qual]).
bool ObjCLifetimeConversion;
- if (S.IsQualificationConversion(FromType, ToType, CStyle,
- ObjCLifetimeConversion)) {
+ if (S.IsFunctionConversion(FromType, ToType, FromType)) {
+ // Function pointer conversions (removing 'noexcept') including removal of
+ // 'noreturn' (Clang extension).
+ SCS.Third = ICK_Function_Conversion;
+ } else if (S.IsQualificationConversion(FromType, ToType, CStyle,
+ ObjCLifetimeConversion)) {
SCS.Third = ICK_Qualification;
SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion;
FromType = ToType;
- CanonFrom = S.Context.getCanonicalType(FromType);
- CanonTo = S.Context.getCanonicalType(ToType);
} else {
// No conversion required
SCS.Third = ICK_Identity;
+ }
- // C++ [over.best.ics]p6:
- // [...] Any difference in top-level cv-qualification is
- // subsumed by the initialization itself and does not constitute
- // a conversion. [...]
- CanonFrom = S.Context.getCanonicalType(FromType);
- CanonTo = S.Context.getCanonicalType(ToType);
- if (CanonFrom.getLocalUnqualifiedType()
- == CanonTo.getLocalUnqualifiedType() &&
- CanonFrom.getLocalQualifiers() != CanonTo.getLocalQualifiers()) {
- FromType = ToType;
- CanonFrom = CanonTo;
- }
+ // C++ [over.best.ics]p6:
+ // [...] Any difference in top-level cv-qualification is
+ // subsumed by the initialization itself and does not constitute
+ // a conversion. [...]
+ QualType CanonFrom = S.Context.getCanonicalType(FromType);
+ QualType CanonTo = S.Context.getCanonicalType(ToType);
+ if (CanonFrom.getLocalUnqualifiedType()
+ == CanonTo.getLocalUnqualifiedType() &&
+ CanonFrom.getLocalQualifiers() != CanonTo.getLocalQualifiers()) {
+ FromType = ToType;
+ CanonFrom = CanonTo;
}
+
SCS.setToType(2, FromType);
if (CanonFrom == CanonTo)
diff --git a/clang/test/CXX/expr/p13.cpp b/clang/test/CXX/expr/p13.cpp
new file mode 100644
index 00000000000..b5f1587f9f2
--- /dev/null
+++ b/clang/test/CXX/expr/p13.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -std=c++1z -verify %s -fexceptions -fcxx-exceptions
+
+struct X {};
+struct Y : X {};
+
+using A = void (*)() noexcept;
+using B = void (*)();
+using C = void (X::*)() noexcept;
+using D = void (X::*)();
+using E = void (Y::*)() noexcept;
+using F = void (Y::*)();
+
+void f(A a, B b, C c, D d, E e, F f, bool k) {
+ a = k ? a : b; // expected-error {{different exception specifications}}
+ b = k ? a : b;
+
+ c = k ? c : d; // expected-error {{different exception specifications}}
+ d = k ? c : d;
+
+ e = k ? c : f; // expected-error {{different exception specifications}}
+ e = k ? d : e; // expected-error {{different exception specifications}}
+ f = k ? c : f;
+ f = k ? d : e;
+
+ const A ak = a;
+ const B bk = b;
+ const A &ak2 = k ? ak : ak;
+ const A &ak3 = k ? ak : bk; // expected-error {{could not bind}}
+ const B &bk3 = k ? ak : bk;
+}
+
+namespace dynamic_exception_spec {
+ // Prior to P0012, we had:
+ // "[...] the target entity shall allow at least the exceptions allowed
+ // by the source value in the assignment or initialization"
+ //
+ // There's really only one way we can coherently apply this to conditional
+ // expressions: this must hold no matter which branch was taken.
+ using X = void (*)() throw(int);
+ using Y = void (*)() throw(float);
+ using Z = void (*)() throw(int, float);
+ void g(X x, Y y, Z z, bool k) {
+ x = k ? X() : Y(); // expected-error {{not superset}}
+ y = k ? X() : Y(); // expected-error {{not superset}}
+ z = k ? X() : Y();
+
+ x = k ? x : y; // expected-error {{not superset}}
+ y = k ? x : y; // expected-error {{not superset}}
+ z = k ? x : y;
+ }
+}
OpenPOWER on IntegriCloud