[Sema] Allow C conversions in C overload logic

C allows for some implicit conversions that C++ does not, e.g. void* ->
char*. This patch teaches clang that these conversions are okay when
dealing with overloads in C.

Differential Revision: http://reviews.llvm.org/D13604

llvm-svn: 249995
This commit is contained in:
George Burgess IV 2015-10-11 20:13:20 +00:00
parent fcc34bdee0
commit 4546181e12
5 changed files with 91 additions and 37 deletions

View File

@ -83,7 +83,8 @@ namespace clang {
ICK_TransparentUnionConversion, ///< Transparent Union Conversions ICK_TransparentUnionConversion, ///< Transparent Union Conversions
ICK_Writeback_Conversion, ///< Objective-C ARC writeback conversion ICK_Writeback_Conversion, ///< Objective-C ARC writeback conversion
ICK_Zero_Event_Conversion, ///< Zero constant to event (OpenCL1.2 6.12.10) ICK_Zero_Event_Conversion, ///< Zero constant to event (OpenCL1.2 6.12.10)
ICK_Num_Conversion_Kinds ///< The number of conversion kinds ICK_C_Only_Conversion, ///< Conversions allowed in C, but not C++
ICK_Num_Conversion_Kinds, ///< The number of conversion kinds
}; };
/// ImplicitConversionRank - The rank of an implicit conversion /// ImplicitConversionRank - The rank of an implicit conversion
@ -95,7 +96,9 @@ namespace clang {
ICR_Promotion, ///< Promotion ICR_Promotion, ///< Promotion
ICR_Conversion, ///< Conversion ICR_Conversion, ///< Conversion
ICR_Complex_Real_Conversion, ///< Complex <-> Real conversion ICR_Complex_Real_Conversion, ///< Complex <-> Real conversion
ICR_Writeback_Conversion ///< ObjC ARC writeback conversion ICR_Writeback_Conversion, ///< ObjC ARC writeback conversion
ICR_C_Conversion ///< Conversion only allowed in the C standard.
/// (e.g. void* to char*)
}; };
ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind);

View File

@ -8292,19 +8292,23 @@ public:
QualType LHSType, QualType LHSType,
QualType RHSType); QualType RHSType);
/// Check assignment constraints and prepare for a conversion of the /// Check assignment constraints and optionally prepare for a conversion of
/// RHS to the LHS type. /// the RHS to the LHS type. The conversion is prepared for if ConvertRHS
/// is true.
AssignConvertType CheckAssignmentConstraints(QualType LHSType, AssignConvertType CheckAssignmentConstraints(QualType LHSType,
ExprResult &RHS, ExprResult &RHS,
CastKind &Kind); CastKind &Kind,
bool ConvertRHS = true);
// CheckSingleAssignmentConstraints - Currently used by // CheckSingleAssignmentConstraints - Currently used by
// CheckAssignmentOperands, and ActOnReturnStmt. Prior to type checking, // CheckAssignmentOperands, and ActOnReturnStmt. Prior to type checking,
// this routine performs the default function/array converions. // this routine performs the default function/array converions, if ConvertRHS
// is true.
AssignConvertType CheckSingleAssignmentConstraints(QualType LHSType, AssignConvertType CheckSingleAssignmentConstraints(QualType LHSType,
ExprResult &RHS, ExprResult &RHS,
bool Diagnose = true, bool Diagnose = true,
bool DiagnoseCFAudited = false); bool DiagnoseCFAudited = false,
bool ConvertRHS = true);
// \brief If the lhs type is a transparent union, check whether we // \brief If the lhs type is a transparent union, check whether we
// can initialize the transparent union with the given expression. // can initialize the transparent union with the given expression.

View File

@ -5376,13 +5376,13 @@ CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) {
return CK_IntegralToFloating; return CK_IntegralToFloating;
case Type::STK_IntegralComplex: case Type::STK_IntegralComplex:
Src = ImpCastExprToType(Src.get(), Src = ImpCastExprToType(Src.get(),
DestTy->castAs<ComplexType>()->getElementType(), DestTy->castAs<ComplexType>()->getElementType(),
CK_IntegralCast); CK_IntegralCast);
return CK_IntegralRealToComplex; return CK_IntegralRealToComplex;
case Type::STK_FloatingComplex: case Type::STK_FloatingComplex:
Src = ImpCastExprToType(Src.get(), Src = ImpCastExprToType(Src.get(),
DestTy->castAs<ComplexType>()->getElementType(), DestTy->castAs<ComplexType>()->getElementType(),
CK_IntegralToFloating); CK_IntegralToFloating);
return CK_FloatingRealToComplex; return CK_FloatingRealToComplex;
case Type::STK_MemberPointer: case Type::STK_MemberPointer:
llvm_unreachable("member pointer type in C"); llvm_unreachable("member pointer type in C");
@ -6867,7 +6867,7 @@ Sema::CheckAssignmentConstraints(SourceLocation Loc,
ExprResult RHSPtr = &RHSExpr; ExprResult RHSPtr = &RHSExpr;
CastKind K = CK_Invalid; CastKind K = CK_Invalid;
return CheckAssignmentConstraints(LHSType, RHSPtr, K); return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false);
} }
/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently /// CheckAssignmentConstraints (C99 6.5.16) - This routine currently
@ -6889,7 +6889,7 @@ Sema::CheckAssignmentConstraints(SourceLocation Loc,
/// Sets 'Kind' for any result kind except Incompatible. /// Sets 'Kind' for any result kind except Incompatible.
Sema::AssignConvertType Sema::AssignConvertType
Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
CastKind &Kind) { CastKind &Kind, bool ConvertRHS) {
QualType RHSType = RHS.get()->getType(); QualType RHSType = RHS.get()->getType();
QualType OrigLHSType = LHSType; QualType OrigLHSType = LHSType;
@ -6911,7 +6911,7 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind); CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind);
if (result != Compatible) if (result != Compatible)
return result; return result;
if (Kind != CK_NoOp) if (Kind != CK_NoOp && ConvertRHS)
RHS = ImpCastExprToType(RHS.get(), AtomicTy->getValueType(), Kind); RHS = ImpCastExprToType(RHS.get(), AtomicTy->getValueType(), Kind);
Kind = CK_NonAtomicToAtomic; Kind = CK_NonAtomicToAtomic;
return Compatible; return Compatible;
@ -6941,7 +6941,7 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
// CK_VectorSplat does T -> vector T, so first cast to the // CK_VectorSplat does T -> vector T, so first cast to the
// element type. // element type.
QualType elType = cast<ExtVectorType>(LHSType)->getElementType(); QualType elType = cast<ExtVectorType>(LHSType)->getElementType();
if (elType != RHSType) { if (elType != RHSType && ConvertRHS) {
Kind = PrepareScalarCast(RHS, elType); Kind = PrepareScalarCast(RHS, elType);
RHS = ImpCastExprToType(RHS.get(), elType, Kind); RHS = ImpCastExprToType(RHS.get(), elType, Kind);
} }
@ -6974,7 +6974,8 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
// Arithmetic conversions. // Arithmetic conversions.
if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && if (LHSType->isArithmeticType() && RHSType->isArithmeticType() &&
!(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) {
Kind = PrepareScalarCast(RHS, LHSType); if (ConvertRHS)
Kind = PrepareScalarCast(RHS, LHSType);
return Compatible; return Compatible;
} }
@ -7099,7 +7100,8 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
// Only under strict condition T^ is compatible with an Objective-C pointer. // Only under strict condition T^ is compatible with an Objective-C pointer.
if (RHSType->isBlockPointerType() && if (RHSType->isBlockPointerType() &&
LHSType->isBlockCompatibleObjCPointerType(Context)) { LHSType->isBlockCompatibleObjCPointerType(Context)) {
maybeExtendBlockObject(RHS); if (ConvertRHS)
maybeExtendBlockObject(RHS);
Kind = CK_BlockPointerToObjCPointerCast; Kind = CK_BlockPointerToObjCPointerCast;
return Compatible; return Compatible;
} }
@ -7225,9 +7227,16 @@ Sema::CheckTransparentUnionArgumentConstraints(QualType ArgType,
} }
Sema::AssignConvertType Sema::AssignConvertType
Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &RHS, Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
bool Diagnose, bool Diagnose,
bool DiagnoseCFAudited) { bool DiagnoseCFAudited,
bool ConvertRHS) {
// If ConvertRHS is false, we want to leave the caller's RHS untouched. Sadly,
// we can't avoid *all* modifications at the moment, so we need some somewhere
// to put the updated value.
ExprResult LocalRHS = CallerRHS;
ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS;
if (getLangOpts().CPlusPlus) { if (getLangOpts().CPlusPlus) {
if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { if (!LHSType->isRecordType() && !LHSType->isAtomicType()) {
// C++ 5.17p3: If the left operand is not of class type, the // C++ 5.17p3: If the left operand is not of class type, the
@ -7276,7 +7285,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &RHS,
CastKind Kind; CastKind Kind;
CXXCastPath Path; CXXCastPath Path;
CheckPointerConversion(RHS.get(), LHSType, Kind, Path, false); CheckPointerConversion(RHS.get(), LHSType, Kind, Path, false);
RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_RValue, &Path); if (ConvertRHS)
RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_RValue, &Path);
return Compatible; return Compatible;
} }
@ -7287,6 +7297,7 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &RHS,
// //
// Suppress this for references: C++ 8.5.3p5. // Suppress this for references: C++ 8.5.3p5.
if (!LHSType->isReferenceType()) { if (!LHSType->isReferenceType()) {
// FIXME: We potentially allocate here even if ConvertRHS is false.
RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
if (RHS.isInvalid()) if (RHS.isInvalid())
return Incompatible; return Incompatible;
@ -7303,7 +7314,7 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &RHS,
CastKind Kind = CK_Invalid; CastKind Kind = CK_Invalid;
Sema::AssignConvertType result = Sema::AssignConvertType result =
CheckAssignmentConstraints(LHSType, RHS, Kind); CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS);
// C99 6.5.16.1p2: The value of the right operand is converted to the // C99 6.5.16.1p2: The value of the right operand is converted to the
// type of the assignment expression. // type of the assignment expression.
@ -7325,7 +7336,8 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &RHS,
return Compatible; return Compatible;
} }
RHS = ImpCastExprToType(E, Ty, Kind); if (ConvertRHS)
RHS = ImpCastExprToType(E, Ty, Kind);
} }
return result; return result;
} }

View File

@ -130,7 +130,11 @@ ImplicitConversionRank clang::GetConversionRank(ImplicitConversionKind Kind) {
ICR_Complex_Real_Conversion, ICR_Complex_Real_Conversion,
ICR_Conversion, ICR_Conversion,
ICR_Conversion, ICR_Conversion,
ICR_Writeback_Conversion ICR_Writeback_Conversion,
ICR_Exact_Match, // NOTE(gbiv): This may not be completely right --
// it was omitted by the patch that added
// ICK_Zero_Event_Conversion
ICR_C_Conversion
}; };
return Rank[(int)Kind]; return Rank[(int)Kind];
} }
@ -162,7 +166,9 @@ static const char* GetImplicitConversionName(ImplicitConversionKind Kind) {
"Complex-real conversion", "Complex-real conversion",
"Block Pointer conversion", "Block Pointer conversion",
"Transparent Union Conversion", "Transparent Union Conversion",
"Writeback conversion" "Writeback conversion",
"OpenCL Zero Event Conversion",
"C specific type conversion"
}; };
return Name[Kind]; return Name[Kind];
} }
@ -1434,13 +1440,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
SCS.CopyConstructor = nullptr; SCS.CopyConstructor = nullptr;
// There are no standard conversions for class types in C++, so // There are no standard conversions for class types in C++, so
// abort early. When overloading in C, however, we do permit // abort early. When overloading in C, however, we do permit them.
if (FromType->isRecordType() || ToType->isRecordType()) { if (S.getLangOpts().CPlusPlus &&
if (S.getLangOpts().CPlusPlus) (FromType->isRecordType() || ToType->isRecordType()))
return false; return false;
// When we're overloading in C, we allow, as standard conversions,
}
// The first conversion can be an lvalue-to-rvalue conversion, // The first conversion can be an lvalue-to-rvalue conversion,
// array-to-pointer conversion, or function-to-pointer conversion // array-to-pointer conversion, or function-to-pointer conversion
@ -1651,7 +1654,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
return true; return true;
} else if (ToType->isEventT() && } else if (ToType->isEventT() &&
From->isIntegerConstantExpr(S.getASTContext()) && From->isIntegerConstantExpr(S.getASTContext()) &&
(From->EvaluateKnownConstInt(S.getASTContext()) == 0)) { From->EvaluateKnownConstInt(S.getASTContext()) == 0) {
SCS.Second = ICK_Zero_Event_Conversion; SCS.Second = ICK_Zero_Event_Conversion;
FromType = ToType; FromType = ToType;
} else { } else {
@ -1690,11 +1693,28 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
} }
SCS.setToType(2, FromType); SCS.setToType(2, FromType);
if (CanonFrom == CanonTo)
return true;
// If we have not converted the argument type to the parameter type, // If we have not converted the argument type to the parameter type,
// this is a bad conversion sequence. // this is a bad conversion sequence, unless we're resolving an overload in C.
if (CanonFrom != CanonTo) if (S.getLangOpts().CPlusPlus || !InOverloadResolution)
return false; return false;
ExprResult ER = ExprResult{From};
auto Conv = S.CheckSingleAssignmentConstraints(ToType, ER,
/*Diagnose=*/false,
/*DiagnoseCFAudited=*/false,
/*ConvertRHS=*/false);
if (Conv != Sema::Compatible)
return false;
SCS.setAllToTypes(ToType);
// We need to set all three because we want this conversion to rank terribly,
// and we don't know what conversions it may overlap with.
SCS.First = ICK_C_Only_Conversion;
SCS.Second = ICK_C_Only_Conversion;
SCS.Third = ICK_C_Only_Conversion;
return true; return true;
} }
@ -4993,6 +5013,7 @@ static bool CheckConvertedConstantConversions(Sema &S,
case ICK_TransparentUnionConversion: case ICK_TransparentUnionConversion:
case ICK_Writeback_Conversion: case ICK_Writeback_Conversion:
case ICK_Zero_Event_Conversion: case ICK_Zero_Event_Conversion:
case ICK_C_Only_Conversion:
return false; return false;
case ICK_Lvalue_To_Rvalue: case ICK_Lvalue_To_Rvalue:

View File

@ -85,3 +85,17 @@ void local() {
void after_local_1(int) __attribute__((overloadable)); // expected-error {{conflicting types}} void after_local_1(int) __attribute__((overloadable)); // expected-error {{conflicting types}}
void after_local_2(int); // expected-error {{must have the 'overloadable' attribute}} void after_local_2(int); // expected-error {{must have the 'overloadable' attribute}}
void after_local_3(int) __attribute__((overloadable)); void after_local_3(int) __attribute__((overloadable));
// Make sure we allow C-specific conversions in C.
void conversions() {
void foo(char *c) __attribute__((overloadable));
void foo(char *c) __attribute__((overloadable, enable_if(c, "nope.jpg")));
void *ptr;
foo(ptr);
void multi_type(unsigned char *c) __attribute__((overloadable));
void multi_type(signed char *c) __attribute__((overloadable));
unsigned char *c;
multi_type(c);
}