Skip to content

[Sema] Penalize conversions to Any. #7233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/Sema/CSRanking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) {
case SK_ScalarPointerConversion:
log << "scalar-to-pointer conversion";
break;
case SK_EmptyExistentialConversion:
log << "empty-existential conversion";
break;
}
log << ")\n";
}
Expand Down
53 changes: 39 additions & 14 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,8 +1558,10 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
break;

case TypeKind::Tuple: {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
// Try the tuple-to-tuple conversion.
conversionsOrFixes.push_back(ConversionRestrictionKind::TupleToTuple);
if (!type1->is<LValueType>())
conversionsOrFixes.push_back(ConversionRestrictionKind::TupleToTuple);
break;
}

Expand All @@ -1568,7 +1570,9 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
case TypeKind::Class: {
auto nominal1 = cast<NominalType>(desugar1);
auto nominal2 = cast<NominalType>(desugar2);
if (nominal1->getDecl() == nominal2->getDecl()) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
if (!type1->is<LValueType>() &&
nominal1->getDecl() == nominal2->getDecl()) {
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
}

Expand All @@ -1579,15 +1583,19 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
auto class2 = cast<ClassDecl>(nominal2->getDecl());

// CF -> Objective-C via toll-free bridging.
if (class1->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
if (!type1->is<LValueType>() &&
class1->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
class2->getForeignClassKind() != ClassDecl::ForeignKind::CFType &&
class1->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::CFTollFreeBridgeToObjC);
}

// Objective-C -> CF via toll-free bridging.
if (class2->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
if (!type1->is<LValueType>() &&
class2->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
class1->getForeignClassKind() != ClassDecl::ForeignKind::CFType &&
class2->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
conversionsOrFixes.push_back(
Expand Down Expand Up @@ -1678,7 +1686,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
auto bound1 = cast<BoundGenericType>(desugar1);
auto bound2 = cast<BoundGenericType>(desugar2);

if (bound1->getDecl() == bound2->getDecl()) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
if (!type1->is<LValueType>() && bound1->getDecl() == bound2->getDecl()) {
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
}
break;
Expand Down Expand Up @@ -1713,12 +1722,13 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// there is at most one non-defaulted element.
// For non-argument tuples, we can do the same conversion but not
// to a tuple with varargs.
if ((tuple2->getNumElements() == 1 &&
!tuple2->getElement(0).isVararg()) ||
(kind >= ConstraintKind::Conversion &&
tuple2->getElementForScalarInit() >= 0 &&
(isArgumentTupleConversion ||
!tuple2->getVarArgsBaseType()))) {
if (!type1->is<LValueType>() &&
((tuple2->getNumElements() == 1 &&
!tuple2->getElement(0).isVararg()) ||
(kind >= ConstraintKind::Conversion &&
tuple2->getElementForScalarInit() >= 0 &&
(isArgumentTupleConversion ||
!tuple2->getVarArgsBaseType())))) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ScalarToTuple);

Expand All @@ -1732,6 +1742,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
type2->getClassOrBoundGenericClass() &&
type1->getClassOrBoundGenericClass()
!= type2->getClassOrBoundGenericClass()) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
}

Expand All @@ -1740,7 +1751,9 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// Don't allow this in operator contexts or we'll end up allowing
// 'T() == U()' for unrelated T and U that just happen to be Hashable.
// We can remove this special case when we implement operator hiding.
if (kind != ConstraintKind::OperatorArgumentConversion) {
if (!type1->is<LValueType>() &&
kind != ConstraintKind::OperatorArgumentConversion) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(
ConversionRestrictionKind::HashableToAnyHashable);
}
Expand Down Expand Up @@ -1800,16 +1813,20 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
}

// Special implicit nominal conversions.
if (kind >= ConstraintKind::Conversion) {
if (!type1->is<LValueType>() &&
kind >= ConstraintKind::Conversion) {
// Array -> Array.
if (isArrayType(desugar1) && isArrayType(desugar2)) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
// Dictionary -> Dictionary.
} else if (isDictionaryType(desugar1) && isDictionaryType(desugar2)) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(
ConversionRestrictionKind::DictionaryUpcast);
// Set -> Set.
} else if (isSetType(desugar1) && isSetType(desugar2)) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(
ConversionRestrictionKind::SetUpcast);
}
Expand Down Expand Up @@ -1981,7 +1998,13 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
// we hit commit_to_conversions below, but we have to add a token restriction
// to ensure we wrap the metatype value in a metatype erasure.
if (concrete && type2->isExistentialType() &&
!type1->is<LValueType>() &&
kind >= ConstraintKind::Subtype) {

// Penalize conversions to Any.
if (kind >= ConstraintKind::Conversion && type2->isAny())
increaseScore(ScoreKind::SK_EmptyExistentialConversion);

conversionsOrFixes.push_back(ConversionRestrictionKind::Existential);
}

Expand All @@ -1992,7 +2015,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
{
BoundGenericType *boundGenericType2;

if (concrete && kind >= ConstraintKind::Subtype &&
if (concrete && !type1->is<LValueType>() &&
kind >= ConstraintKind::Subtype &&
(boundGenericType2 = type2->getAs<BoundGenericType>())) {
auto decl2 = boundGenericType2->getDecl();
if (auto optionalKind2 = decl2->classifyAsOptionalType()) {
Expand Down Expand Up @@ -2025,6 +2049,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
ConversionRestrictionKind::ValueToOptional);
}
}

}

// A value of type T! can be (unsafely) forced to U if T
Expand Down
4 changes: 3 additions & 1 deletion lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,10 @@ enum ScoreKind {
SK_ScalarPointerConversion,
/// A conversion from an array to a pointer of matching element type.
SK_ArrayPointerConversion,
/// A conversion to an empty existential type ('Any' or '{}').
SK_EmptyExistentialConversion,

SK_LastScoreKind = SK_ArrayPointerConversion,
SK_LastScoreKind = SK_EmptyExistentialConversion,
};

/// The number of score kinds.
Expand Down
7 changes: 7 additions & 0 deletions test/Constraints/array_literal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,10 @@ let router = Company(
}
]
)

// Infer [[Int]] for SR3786aa.
// FIXME: As noted in SR-3786, this was the behavior in Swift 3, but
// it seems like the wrong choice and is less by design than by
// accident.
let SR3786a: [Int] = [1, 2, 3]
let SR3786aa = [SR3786a.reversed(), SR3786a]
10 changes: 3 additions & 7 deletions test/Constraints/diag_ambiguities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,9 @@ func rdar29691909(o: AnyObject) -> Any? {
return rdar29691909_callee(o) // expected-error{{ambiguous use of 'rdar29691909_callee'}}
}

// FIXME: The fix for this broke other things. We want to get this
// test case running again, though.
// Ensure that we decay Any! to Any? rather than allowing Any!-to-Any
// conversion directly and ending up with an ambiguity here.
//func rdar29907555(_ value: Any!) -> String {
// return "\(value)" // no error
//}
func rdar29907555(_ value: Any!) -> String {
return "\(value)" // no error
}

struct SR3715 {
var overloaded: Int!
Expand Down
9 changes: 9 additions & 0 deletions test/Constraints/overload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,12 @@ func overloadedMethod<T>() {}

overloadedMethod()
// expected-error@-1 {{missing argument for parameter 'n' in call}}

// Ensure we select the overload of '??' returning T? rather than T.
func SR3817(_ d: [String : Any], _ s: String, _ t: String) -> Any {
if let r = d[s] ?? d[t] {
return r
} else {
return 0
}
}