Skip to content

[TypeChecker] Treat tuples specially while validating checked casts #28910

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
Jan 6, 2020
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
78 changes: 45 additions & 33 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4075,27 +4075,32 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
}
}

// Check for casts between specific concrete types that cannot succeed.
if (auto toElementType = ConstraintSystem::isArrayType(toType)) {
if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) {
switch (typeCheckCheckedCast(*fromElementType, *toElementType,
CheckedCastContextKind::None, dc,
SourceLoc(), nullptr, SourceRange())) {
case CheckedCastKind::Coercion:
return CheckedCastKind::Coercion;
auto checkElementCast = [&](Type fromElt, Type toElt,
CheckedCastKind castKind) -> CheckedCastKind {
switch (typeCheckCheckedCast(fromElt, toElt, CheckedCastContextKind::None,
dc, SourceLoc(), nullptr, SourceRange())) {
case CheckedCastKind::Coercion:
return CheckedCastKind::Coercion;

case CheckedCastKind::BridgingCoercion:
return CheckedCastKind::BridgingCoercion;
case CheckedCastKind::BridgingCoercion:
return CheckedCastKind::BridgingCoercion;

case CheckedCastKind::ArrayDowncast:
case CheckedCastKind::DictionaryDowncast:
case CheckedCastKind::SetDowncast:
case CheckedCastKind::ValueCast:
return CheckedCastKind::ArrayDowncast;
case CheckedCastKind::ArrayDowncast:
case CheckedCastKind::DictionaryDowncast:
case CheckedCastKind::SetDowncast:
case CheckedCastKind::ValueCast:
return castKind;

case CheckedCastKind::Unresolved:
return failed();
}
case CheckedCastKind::Unresolved:
return failed();
}
};

// Check for casts between specific concrete types that cannot succeed.
if (auto toElementType = ConstraintSystem::isArrayType(toType)) {
if (auto fromElementType = ConstraintSystem::isArrayType(fromType)) {
return checkElementCast(*fromElementType, *toElementType,
CheckedCastKind::ArrayDowncast);
}
}

Expand Down Expand Up @@ -4165,24 +4170,31 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,

if (auto toElementType = ConstraintSystem::isSetType(toType)) {
if (auto fromElementType = ConstraintSystem::isSetType(fromType)) {
switch (typeCheckCheckedCast(*fromElementType, *toElementType,
CheckedCastContextKind::None, dc,
SourceLoc(), nullptr, SourceRange())) {
case CheckedCastKind::Coercion:
return CheckedCastKind::Coercion;

case CheckedCastKind::BridgingCoercion:
return CheckedCastKind::BridgingCoercion;

case CheckedCastKind::ArrayDowncast:
case CheckedCastKind::DictionaryDowncast:
case CheckedCastKind::SetDowncast:
case CheckedCastKind::ValueCast:
return CheckedCastKind::SetDowncast;
return checkElementCast(*fromElementType, *toElementType,
CheckedCastKind::SetDowncast);
}
}

case CheckedCastKind::Unresolved:
if (auto toTuple = toType->getAs<TupleType>()) {
if (auto fromTuple = fromType->getAs<TupleType>()) {
if (fromTuple->getNumElements() != toTuple->getNumElements())
return failed();

for (unsigned i = 0, n = toTuple->getNumElements(); i != n; ++i) {
const auto &fromElt = fromTuple->getElement(i);
const auto &toElt = toTuple->getElement(i);

if (fromElt.getName() != toElt.getName())
return failed();

auto result = checkElementCast(fromElt.getType(), toElt.getType(),
CheckedCastKind::ValueCast);

if (result == CheckedCastKind::Unresolved)
return result;
}

return CheckedCastKind::ValueCast;
}
}

Expand Down
16 changes: 16 additions & 0 deletions test/Constraints/casts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,19 @@ func compare<T>(_: T, _: T) {} // expected-note {{'compare' declared here}}
func compare<T>(_: T?, _: T?) {}

_ = nil? as? Int?? // expected-error {{nil literal cannot be the source of a conditional cast}}

func test_tuple_casts_no_warn() {
struct Foo {}

let arr: [(Any, Any)] = [(Foo(), Foo())]
let tup: (Any, Any) = (Foo(), Foo())

_ = arr as! [(Foo, Foo)] // Ok
_ = tup as! (Foo, Foo) // Ok

_ = arr as! [(Foo, Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(Foo, Foo, Foo)]' always fails}}
_ = tup as! (Foo, Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(Foo, Foo, Foo)' always fails}}

_ = arr as! [(a: Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(a: Foo, Foo)]' always fails}}
_ = tup as! (a: Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(a: Foo, Foo)' always fails}}
}