forked from OSchip/llvm-project
Revert r284753 "[c++1z] Teach composite pointer type computation how to compute the composite"
It caused PR30749. llvm-svn: 284778
This commit is contained in:
parent
ae818501b7
commit
2b81f42a76
|
@ -145,8 +145,7 @@ namespace clang {
|
|||
/// pointer-to-member conversion, or boolean conversion.
|
||||
ImplicitConversionKind Second : 8;
|
||||
|
||||
/// Third - The third conversion can be a qualification conversion
|
||||
/// or a function conversion.
|
||||
/// Third - The third conversion can be a qualification conversion.
|
||||
ImplicitConversionKind Third : 8;
|
||||
|
||||
/// \brief Whether this is the deprecated conversion of a
|
||||
|
|
|
@ -8954,15 +8954,13 @@ 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 ConvertArgs = true);
|
||||
bool *NonStandardCompositeType = nullptr);
|
||||
QualType FindCompositePointerType(SourceLocation Loc,
|
||||
ExprResult &E1, ExprResult &E2,
|
||||
bool *NonStandardCompositeType = nullptr,
|
||||
bool ConvertArgs = true) {
|
||||
bool *NonStandardCompositeType = nullptr) {
|
||||
Expr *E1Tmp = E1.get(), *E2Tmp = E2.get();
|
||||
QualType Composite = FindCompositePointerType(
|
||||
Loc, E1Tmp, E2Tmp, NonStandardCompositeType, ConvertArgs);
|
||||
QualType Composite = FindCompositePointerType(Loc, E1Tmp, E2Tmp,
|
||||
NonStandardCompositeType);
|
||||
E1 = E1Tmp;
|
||||
E2 = E2Tmp;
|
||||
return Composite;
|
||||
|
|
|
@ -3610,6 +3610,16 @@ 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()) {
|
||||
|
@ -3856,7 +3866,6 @@ 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:
|
||||
|
@ -3869,16 +3878,6 @@ 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.
|
||||
|
@ -5394,17 +5393,6 @@ 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()) {
|
||||
LTy = FindCompositePointerType(QuestionLoc, LHS, RHS, nullptr,
|
||||
/*ConvertArgs*/false);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -5459,14 +5447,6 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -5537,78 +5517,6 @@ 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
|
||||
|
@ -5623,12 +5531,9 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1,
|
|||
/// 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 ConvertArgs) {
|
||||
bool *NonStandardCompositeType) {
|
||||
if (NonStandardCompositeType)
|
||||
*NonStandardCompositeType = false;
|
||||
|
||||
|
@ -5655,18 +5560,16 @@ 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)) {
|
||||
if (ConvertArgs)
|
||||
E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType()
|
||||
? CK_NullToMemberPointer
|
||||
: CK_NullToPointer).get();
|
||||
E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType()
|
||||
? CK_NullToMemberPointer
|
||||
: CK_NullToPointer).get();
|
||||
return T1;
|
||||
}
|
||||
if (T2IsPointerLike &&
|
||||
E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
|
||||
if (ConvertArgs)
|
||||
E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType()
|
||||
? CK_NullToMemberPointer
|
||||
: CK_NullToPointer).get();
|
||||
E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType()
|
||||
? CK_NullToMemberPointer
|
||||
: CK_NullToPointer).get();
|
||||
return T2;
|
||||
}
|
||||
|
||||
|
@ -5712,8 +5615,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
|
|||
// exists.
|
||||
SmallVector<unsigned, 4> QualifierUnion;
|
||||
SmallVector<std::pair<const Type *, const Type *>, 4> MemberOfClass;
|
||||
QualType Composite1 = T1;
|
||||
QualType Composite2 = T2;
|
||||
QualType Composite1 = Context.getCanonicalType(T1);
|
||||
QualType Composite2 = Context.getCanonicalType(T2);
|
||||
unsigned NeedConstBefore = 0;
|
||||
while (true) {
|
||||
const PointerType *Ptr1, *Ptr2;
|
||||
|
@ -5759,41 +5662,6 @@ 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
|
||||
|
@ -5843,28 +5711,25 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
|
|||
E1ToC(S, Entity, Kind, E1), E2ToC(S, Entity, Kind, E2),
|
||||
Viable(E1ToC && E2ToC) {}
|
||||
|
||||
bool perform() {
|
||||
QualType perform() {
|
||||
ExprResult E1Result = E1ToC.Perform(S, Entity, Kind, E1);
|
||||
if (E1Result.isInvalid())
|
||||
return true;
|
||||
return QualType();
|
||||
E1 = E1Result.getAs<Expr>();
|
||||
|
||||
ExprResult E2Result = E2ToC.Perform(S, Entity, Kind, E2);
|
||||
if (E2Result.isInvalid())
|
||||
return true;
|
||||
return QualType();
|
||||
E2 = E2Result.getAs<Expr>();
|
||||
|
||||
return false;
|
||||
return Composite;
|
||||
}
|
||||
};
|
||||
|
||||
// Try to convert to each composite pointer type.
|
||||
Conversion C1(*this, Loc, E1, E2, Composite1);
|
||||
if (C1.Viable && Context.hasSameType(Composite1, Composite2)) {
|
||||
if (ConvertArgs && C1.perform())
|
||||
return QualType();
|
||||
return C1.Composite;
|
||||
}
|
||||
if (C1.Viable && Context.hasSameType(Composite1, Composite2))
|
||||
return C1.perform();
|
||||
Conversion C2(*this, Loc, E1, E2, Composite2);
|
||||
|
||||
if (C1.Viable == C2.Viable) {
|
||||
|
@ -5875,10 +5740,7 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc,
|
|||
}
|
||||
|
||||
// Convert to the chosen type.
|
||||
if (ConvertArgs && (C1.Viable ? C1 : C2).perform())
|
||||
return QualType();
|
||||
|
||||
return C1.Viable ? C1.Composite : C2.Composite;
|
||||
return (C1.Viable ? C1 : C2).perform();
|
||||
}
|
||||
|
||||
ExprResult Sema::MaybeBindToTemporary(Expr *E) {
|
||||
|
|
|
@ -1405,7 +1405,6 @@ 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();
|
||||
|
@ -1418,13 +1417,8 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
|
|||
CanTo = CanTo.getAs<BlockPointerType>()->getPointeeType();
|
||||
CanFrom = CanFrom.getAs<BlockPointerType>()->getPointeeType();
|
||||
} else if (TyClass == Type::MemberPointer) {
|
||||
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();
|
||||
CanTo = CanTo.getAs<MemberPointerType>()->getPointeeType();
|
||||
CanFrom = CanFrom.getAs<MemberPointerType>()->getPointeeType();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -1763,6 +1757,10 @@ 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)) {
|
||||
|
@ -1784,36 +1782,34 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
|
|||
}
|
||||
SCS.setToType(1, FromType);
|
||||
|
||||
// The third conversion can be a function pointer conversion or a
|
||||
// qualification conversion (C++ [conv.fctptr], [conv.qual]).
|
||||
QualType CanonFrom;
|
||||
QualType CanonTo;
|
||||
// The third conversion can be a qualification conversion (C++ 4p1).
|
||||
bool 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)) {
|
||||
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. [...]
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
SCS.setToType(2, FromType);
|
||||
|
||||
if (CanonFrom == CanonTo)
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue