Skip to content

[SR-7497] [stdlib] Fix KeyPaths hashing #17467

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 25, 2018
Merged

[SR-7497] [stdlib] Fix KeyPaths hashing #17467

merged 3 commits into from
Jun 25, 2018

Conversation

dmcyk
Copy link
Contributor

@dmcyk dmcyk commented Jun 24, 2018

  • The cause of hashing regression was a shadowed hasher argument in component hashing, so the component type/offset wasn't taken into account
  • Currently only path components are hashed, so paths with distinct types but properties of equal kind and offset result in same hash values. That differs from equatable implementation as type of two key paths must be the same to consider them equal.
    So I thought it'd make sense to include the type metadata pointer while hashing. As far as I understand it, the metadata pointer doesn't change during program execution.

Regarding code:
I'm not sure if type(of: self) as Any.Type cast is necessary.
Quickly running REPL trying to do unsafeBitCast(type(of: Int(0)), to: Int.self)crashes, casting the type(of:) result to Any.Type helps though.
Is this caused by value vs ref type difference? Doing so with a class object works.
I was looking at _getTypeName and other core functions, but I couldn't really find a place where Any.Type is guaranteed to be const Metadata* and how Any.Type differs from e.g. Int.Type.
Is it somehow implemented at SIL level? Would be great if someone could maybe give me a tip where to look for information/code regarding this.

Resolves SR-7497.

P.S. That's my first attempt at contribution to Swift. Hello everyone 👋

dmcyk added 2 commits June 25, 2018 00:26
Removes shadowed `hasher` argument in component hashing.
Include type metadata pointer in hashing.
@huonw huonw requested a review from jckarter June 25, 2018 00:06
@huonw
Copy link
Contributor

huonw commented Jun 25, 2018

Quickly running REPL trying to do unsafeBitCast(type(of: Int(0)), to: Int.self)crashes, casting the type(of:) result to Any.Type helps though.
Is this caused by value vs ref type difference? Doing so with a class object works.

The metatype object for something that can't be subclassed (or otherwise store interesting information about the type) is zero-sized. The (meta)type Int.Type has just one value Int.self, meaning it is like ()/Void and has the same size. In contrast, given

class Super {}
class Sub1: Super {}
class Sub2: Super {}

The type Super.Type has (at least) three possible values Super.self, Sub1.self and Sub2.self. This is, I believe, represented by the values of Super.Type being a pointer to the actual metadata. I believe metatypes of protocol existentials and Any.Type are similar, and they are a pointer to the actual information about the type in the existential as you suspect. Someone like @jckarter will have to confirm this, though.

As I guess you might already know, unsafeBitCast only works between things of the same size, which is why it fails for Int.Type (size 0) -> Int (size 8).

(Hello and welcome!)

@dmcyk
Copy link
Contributor Author

dmcyk commented Jun 25, 2018

ah, I see. I misunderstood it. I thought Int.Type was representing the type metadata itself, rather than a meta type.
I found init_existential_metatype instruction in SIL reference, that says that existential meta types store the type metadata. There's also a note on Metatype types, saying indeed that concrete meta types require no storage. Thanks!

@@ -59,6 +59,7 @@ public class AnyKeyPath: Hashable, _AppendKeyPath {
/// of this instance.
@_effects(releasenone)
final public func hash(into hasher: inout Hasher) {
hasher.combine(unsafeBitCast(type(of: self) as Any.Type, to: Int.self))
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be written ObjectIdentifier(type(of: self)).hash(into: &hasher)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good to know, thanks 👌

@jckarter
Copy link
Contributor

Nice catch!

Uses ObjectIdentifier to extract metatype pointer,
instead of unsafe pointer casting.
Copy link
Member

@lorentey lorentey left a comment

Choose a reason for hiding this comment

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

Thanks for spotting this!

@lorentey
Copy link
Member

@swift-ci please smoke test and merge

1 similar comment
@lorentey
Copy link
Member

@swift-ci please smoke test and merge

@dmcyk
Copy link
Contributor Author

dmcyk commented Jun 25, 2018

@lorentey my pleasure 😁 I guess this could be merged into 4.2 too, right?

@lorentey
Copy link
Member

@dmcyk Yes, we should get it into 4.2! I'll cherry pick it after it lands on master.

@swift-ci swift-ci merged commit 461b293 into swiftlang:master Jun 25, 2018
@dmcyk dmcyk deleted the sr-7497 branch June 25, 2018 18:52
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.

5 participants