Skip to content

Run source-typer on crystal standard library, give or take #15682

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Vici37
Copy link
Contributor

@Vici37 Vici37 commented Apr 18, 2025

This runs the https://github.com/Vici37/cr-source-typer project on the crystal source code with command:

CRYSTAL_PATH="./src" ./typer spec/std_spec.cr --error-trace --exclude src/crystal/ --stats --progress src 

Followed by repeated:

make spec # error
git checkout <next offending file>

Until specs passed and a basic make call all work.

I have done no additional fixing beyond the above - the way that the standard lib is loosely coupled with itself means that the order of requires isn't well structured, and so some types were present in restrictions before the relevant require was hit. Some methods have hilariously long type restrictions as well.

This PR should not be as accepted as it is, but represents an interesting view of the internals of the compiler and standard library that I wanted to share.

@straight-shoota straight-shoota marked this pull request as draft April 18, 2025 06:17
Copy link
Contributor

@ysbaddaden ysbaddaden left a comment

Choose a reason for hiding this comment

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

We can't merge as-is it will introduce countless breaking changes, yet, I think it's interesting to notice some complex unions/tuples that could get simplified (less types), as well as optimization opportunities (expect slice vs passing static array).

strict_encode data, CHARS_STD, pad: true
end

private def strict_encode(data, alphabet, pad = false)
private def strict_encode(data : Slice(UInt8) | StaticArray(UInt8, 5) | String | StaticArray(UInt8, 20) | StaticArray(UInt8, 16), alphabet : String, pad : Bool = false) : String
Copy link
Contributor

Choose a reason for hiding this comment

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

The static arrays are interesting: they outline that the method should really just take a slice, and there are opportunities for optimizations.

Even the string in the other methods are interesting. There could be a specific overload that would call the slice method.

Copy link
Member

Choose a reason for hiding this comment

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

Ideally, we should be able to express a type restriction for types that have a #to_slice : Bytes method (cf. #934).

Copy link
Member

@straight-shoota straight-shoota Apr 18, 2025

Choose a reason for hiding this comment

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

I'm not sure about runtime optimizations because private methods are typically inlined anyway, so the static array here likely won't be copied around.
But for sure the compiler would have an easier time if it has to deal with only one method accepting Bytes instead of a multitude of different StaticArray instance types.

select_impl(ops, true)
end

private def self.select_impl(ops : Indexable(SelectAction), non_blocking)
private def self.select_impl(ops : Indexable(SelectAction), non_blocking : Bool) : Tuple(Int32, Channel::NotReady | Int32) | Tuple(Int32, Channel::NotReady | Nil) | Tuple(Int32, Channel::NotReady | String) | Tuple(Int32, Bool | Channel::NotReady | String) | Tuple(Int32, Channel::NotReady | String | Nil) | Tuple(Int32, Bool | Channel::NotReady | String | Nil) | Tuple(Int32, Bool | Channel::NotReady) | Tuple(Int32, Bool | Channel::NotReady | Nil) | Tuple(Int32, Channel::NotReady | Int32 | Nil) | Tuple(Int32, Channel::NotReady | Exception | Nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

Urgh, tuples are nice but they quickly create lots of types. It might be interesting to introduce a type, here 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seeing the size of the union is a good indication there's confusion on what's expected of this method. Could also be a good opportunity to see why some of the types show up like this as well.

@straight-shoota
Copy link
Member

Related:

There are lots of trivial type enhancements which should be straightforward to merge in.
For example, simple return types like Nil, or that #== returns Bool.
Anything a bit challenging (probably most union types) can be deferred for later.

Following the example of #10575, it would be helpful to split these changes into semantic groups. That makes it easier to review and merge.

Once the simple cases are resolved, we can more clearly make out and discuss the more complex ones.

Let me know if you want to proceed with that, or need any help.

@Vici37
Copy link
Contributor Author

Vici37 commented Apr 19, 2025

Agreed! I could add some filtering options or thresholds - add restrictions if the number of types in the union is less than N, say.

I'll work on making some smaller PRs over some smaller clusters of related code. I also thought seeing the sheer mass of these changes amusing enough to want to share them in all their glory 😁

@Vici37
Copy link
Contributor Author

Vici37 commented Apr 20, 2025

Spent about 30 minutes tonight creating some PRs for some directories:

#15687 - oauth
#15688 - benchmark
#15689 - big

Let me know what you think :)

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.

3 participants