Skip to content

Add type restrictions to io#15698

Merged
straight-shoota merged 9 commits intocrystal-lang:masterfrom
Vici37:add-type-restrictions-to-io
Aug 15, 2025
Merged

Add type restrictions to io#15698
straight-shoota merged 9 commits intocrystal-lang:masterfrom
Vici37:add-type-restrictions-to-io

Conversation

@Vici37
Copy link
Contributor

@Vici37 Vici37 commented Apr 22, 2025

This is the output of compiling cr-source-typer and running it with the below incantation:

CRYSTAL_PATH="./src" ./typer spec/std_spec.cr \
  --error-trace --exclude src/crystal/ \
  --stats --progress \
  --union-size-threshold 2 \
  --ignore-private-defs \
  --ignore-protected-defs \
  src/io

This is related to #15682 .

I also reran the above command but without the --union-size-threshold 2, and updated several long union types to IO by itself.

src/io.cr Outdated

# Same as `pos`.
def tell
def tell : Int64 | Int32
Copy link
Contributor

Choose a reason for hiding this comment

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

This look very odd to me. Especially as pos is noreturn. Where does this union type come from?

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 see Memory#pos return Int32 and Buffered#pos return Int64. The side effect being this wrapping def indirectly inherits the return types of those methods 🤷

Maybe better to omit the return type restriction in this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

Either that, or declare it to be typeof(pos), if that is possible 🤔

Comment on lines 81 to 83
def blocking=(value : Bool) : Int32?
self.system_blocking = value
end
Copy link
Contributor

Choose a reason for hiding this comment

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

These combination of input and return types should not be correct. The return type of an assignment should be the value assigned. Does system_blocking= do anything weird?

Copy link
Member

Choose a reason for hiding this comment

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

system_blocking= may leak the return value of fcntl.
We should probably return value (c.f. #10083).

For this PR, I'd suggest to just set the return type restriction to Nil.

Copy link
Contributor Author

@Vici37 Vici37 Apr 24, 2025

Choose a reason for hiding this comment

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

Per #10083, I could also update this method to return Bool and put value at the end?

Edit: Scratch that, I'll just do what you suggest in this case, and let that issue take care of this :)

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's best to keep this PR focused on adding type restrictions, without changing anything else.

end

def self.check_invalid(invalid) : Nil
def self.check_invalid(invalid : Symbol?) : Nil
Copy link
Collaborator

@ysbaddaden ysbaddaden May 5, 2025

Choose a reason for hiding this comment

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

Thought (non-blocking): we should refactor to use an enum instead of a symbol. That would validate the value at comptime, instead of delaying to runtime, and it would be backward compatible, since symbols are automatically transformed into enums.

class IO
  struct EncodingOptions
    enum Invalid
      Skip
    end

    getter name : String
    getter invalid : Invalid?

    def initialize(@name : String, @invalid : Invalid?)
    end
  end

Copy link
Collaborator

@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.

There are still a few details!

@straight-shoota straight-shoota added this to the 1.18.0 milestone Aug 12, 2025
@straight-shoota straight-shoota merged commit c60519a into crystal-lang:master Aug 15, 2025
40 checks passed
straight-shoota added a commit that referenced this pull request Aug 17, 2025
`#system_close_on_exec=` (and thus `#close_on_exec=`) returns `Nil` on Windows. But #15698 added a type-restriction to `Bool`. We didn't notice this discrepancy before, because this method was never called on Windows until we enabled the specs for that in #14716.

Changing the return value to `false` on Windows should be the correct behaviour. If the parameter is `true`, the method raises.

This is a classic integration issue. It could've been avoided by testing each change against current master directly before merging it. But luckily they are pretty rare, so there's no urgent need to do something about it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants