mismatched typing for TypeCoerce[<T>].new.from
(with Tapioca)
#77
Labels
bug
Something isn't working
TypeCoerce[<T>].new.from
(with Tapioca)
#77
Deep Dive Summary
Describe the bug:
Right at the end of my internship, we successfully moved infra-tasks to fully use the new Tapioca for RBI gen. Awesome! However, I did run into one hiccup (that I can no longer see :/ ) in using Tapioca to generate RBIs - in particular, that the RBI it generated is incompatible with sorbet-coerce!
After doing some thinking, I think that Tapioca is right, and our provided/canonical RBI is wrong (or at the very least, does not fully describe sorbet-coerce's behaviour). Here's the (relevant portion of) the RBI that's in
bundled_rbi
/ insorbet-typed
/ committed in traject, infra-tasks, etc.:In other words, we are marking
TypeCoerce
as a generic, and so something likeTypeCoerce[<T>].new.from(...)
should behave in this order:TypeCoerce[<T>]
is the generic type with thetype_member
TypeCoerce[<T>].new
creates an instanceTypeCoerce[<T>].new.from(...)
uses the sig we just defined, and so it must be of type<T>
Compare this with what the code actually does. Looking at
sorbet-coerce.rb
:I don't have the context on why this was implemented this way (my guess - Sorbet's generics API wasn't finalized yet).
However, when Tapioca does its runtime introspection, it says:
TypeCoerce[<T>]
is not a generic type: it's overloaded (?) and returns an instance ofTypeCoerce::Converter
TypeCoerce::Converter
is not typed (and has no sigs). Let's call itT.untyped
then!TypeCoerce[<T>].new
is calling.new
onT.untyped
. no bueno (or,T.untyped
)TypeCoerce[<T>].new.from(...)
is calling.from
onT.untyped
. double no bueno (or,T.untyped
)!sig
that we wrote! :(This creates a large
T.untyped
chain, and means that the result of the coercion isT.untyped
- surely not what we want.In my opinion, this is incorrect typing: while it's true at a very black-boxed level, any amount of runtime introspection causes tension with sorbet.
I'm not 100% sure this is a correct diagnosis of the behaviour - and it's harder for me to confirm now that I can't see the codebase. But, I'm pretty confident that there's something wrong with our bundled types, and hopefully this is at least a good start!
(and, note that this bug doesn't affect the runtime (like the other sorbet-coerce bug I ran into) - since at runtime, our expression still typechecks)
potential solutions
I think there are two ways forward:
Converter
!Converter
use sorbet generics under-the-hood (instead of passing the type as a field)things included in the issue template
Steps to reproduce:
Create a new project that consumes sorbet-coerce (and uses it somewhere). Then,
bundle exec tapioca init
(implicitly - runstapioca gem
)bundle exec srb tc
- the static typecheck failsNote that, at runtime, the code works! Though, it seems like this is somewhat undefined behaviour (re: Jake Zimmerman's previous comment in the Sorbet slack about Sorbet not supporting programming at the type-level).
Expected behavior:
Static typecheck should pass.
Actual behaviour:
Static typecheck fails.
Versions:
The text was updated successfully, but these errors were encountered: