Skip to content

[Sema] More aggressive consideration of conditionally defined types in expressions #17168

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 3 commits into from
Jun 14, 2018

Conversation

huonw
Copy link
Contributor

@huonw huonw commented Jun 13, 2018

See the commit messages.

This should complete the expression-type-checking sides of https://bugs.swift.org/browse/SR-5440 and https://bugs.swift.org/browse/SR-7976 and rdar://problem/41063870. We don't validate conditionality for decls at all yet.

@huonw huonw requested review from xedin and DougGregor June 13, 2018 11:55
if (parentTy && extension && extension->isConstrainedExtension()) {
auto subMap = NAT->getSubstitutionMap();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand what this substitution map is doing (why are conformances looked up in this one, but types looked up in the context one?), so I just guessed at the appropriate replacement. Thoughts?

Copy link
Contributor

@xedin xedin Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's happening because extension has more context over the types it mentions like in the example I've added:

class A<T, U> {}

extension A where T == [U], U: P {
  typealias S1 = Int
}

class B<U> : A<[U], U> {}

_ = B<C>.S1()

Extension's substitution map would have relationship between T, U and conformance to P but the parent one wouldn't (it only knows about T and U), so we need to use substitution map from the extension to get conformances (without using lookupConformance) but type substitutions have to come from the parent because otherwise they are going to be replaced with types from the where clause of extension which we don't want because that's exactly what we are trying to check :)

Copy link
Contributor Author

@huonw huonw Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see.

This "little" detail seems to be fairly important; this code doesn't handle the nested type being generic, since it ends up looking at the parent's parameters and not including any info about the child's:

struct X<T> {}
extension X where T == Int {
    struct Y<U> {}
}

_ = X<Int>.Y<Float>()
_ = X<Float>.Y<Int>()

This case is handled when the parameters are applied though, so I'm thinking that we just avoid it by only doing this work for non-generic types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think original code made sense for typealias because the assumption was that we only needed to check if declaration context requirements held, it also seems in case of inner generic you don't really need to do anything special because open* would bind contextual types appropriately...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it also seems in case of inner generic you don't really need to do anything special because open* would bind contextual types appropriately...

Yeah, that's the approach I've now taken and it seems to work.

@huonw
Copy link
Contributor Author

huonw commented Jun 13, 2018

@swift-ci please smoke test

@huonw
Copy link
Contributor Author

huonw commented Jun 13, 2018

Hey @AnthonyLatsis, this does half the work on https://bugs.swift.org/browse/SR-5440 (I only noticed you had it assigned after doing this, since I was working on it from the angle of https://bugs.swift.org/browse/SR-7976; sorry!), but there's still checking of validity of these conditional bounds when these types are used in decls, e.g. let _: Generic<T>.SomethingConditional (and the var v and var hmm from 5440). Have you made much progress from your side? Are you interested in working on the decl checking?

@AnthonyLatsis
Copy link
Collaborator

AnthonyLatsis commented Jun 13, 2018

@huon I was getting there. I didn’t really know where exactly to look at, but now I have a head start, thanks! I’ll be glad to deal with the sema part.

Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you! And apparently the problem is actually bigger than just typealias.

huonw added 3 commits June 14, 2018 17:34
…ypes.

One can have arbitrary types defined in a constrained extension, not just
typealiases, e.g.

struct Foo<T> {}
extension Foo where T == Int {
    struct X {}
}

A mention of `Foo<String>.X` should be flagged, since String != Int. Previously
this worked (in expressions) only if `X` was a typealias, and with this patch
the above example is flagged too.

Part of https://bugs.swift.org/browse/SR-5440 and
https://bugs.swift.org/browse/SR-7976 and rdar://problem/41063870.
An expression like Type.Member will first do a normal lookup, and, if only
associated types could be found, will then do associated type inference on
them.

Previously the types of these would be returned directly, but this loses the
sugar associated with the implicit typealias that is created. The sugar is
necessary to ensure that things like the checking of conditional requirements is
satisfied, such as:

    protocol P {
      associatedtype T
    }
    struct X<T> {}
    extension X: P where T == Int {}
    let _ = X<String>.T.self

If the underlying type 'String' (or even 'Int') of 'X<...>.T' is returned, then
the above ends up being perfectly fine, because those are always available, but
that expression itself isn't quite right because the T typealias is conditional.

With this change, once associatedtype inference runs, the typealias that it
creates is found, and the type is mapped to have all the right sugar, meaning
the above is flagged. (This hints at an extra exciting bit of the original
behaviour: if the `let ...` line was copied so that there were two, the second
one gets the expected error, because it doesn't need to run inference and just
finds the typealiases created by the first line.)

Part of https://bugs.swift.org/browse/SR-5440 and
https://bugs.swift.org/browse/SR-7976 and rdar://problem/41063870.
@huonw huonw force-pushed the validate-conditional-type-in-expr branch from fea0e56 to 28460bc Compare June 14, 2018 07:37
@huonw
Copy link
Contributor Author

huonw commented Jun 14, 2018

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - fea0e56c8d4de837194b6c3cd091051b134839e7

@huonw
Copy link
Contributor Author

huonw commented Jun 14, 2018

@swift-ci please test macOS

@huonw huonw merged commit e4d306f into swiftlang:master Jun 14, 2018
@huonw huonw deleted the validate-conditional-type-in-expr branch June 14, 2018 23:00
@huonw huonw restored the validate-conditional-type-in-expr branch June 21, 2018 00:59
@huonw huonw deleted the validate-conditional-type-in-expr branch June 21, 2018 01:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants