Tapioca DSL crashes when using T::Struct
containing generic members #2130
Description
Summary
When running tapioca dsl
in a Rails app that
- Overrides
T::Configuration.call_validation_error_handler
during application load with a handler that raises errors - Contains a
T::Struct
with a member whose type is a generic interface - Instantiates that struct during app startup with a concrete implementation of the generic interface
Tapioca will crash while trying to create the struct because of a type mismatch error between the interface and the implementation.
I created a repository containing what seems to be the absolute minimum configuration necessary to activate this bug. You can find it here: https://github.com/amanda-mitchell/tapioca-bug-repro
The short version of my analysis is that while most type checks are suppressed by Tapioca setting the default check level to :never
, members declared on T::Struct
don't respect this setting, so they still produce type errors.
Workarounds
Although I'd love to see this get a long term fix, I'd also like to document some potential workarounds for folks who might run across this in the meantime.
Use setter_validate
T::Struct
contains an undocumented option for member declarations called setter_validate
, and this is expected to a proc that performs validation side effects. Providing an empty (no-op) lambda will force the implementation of T::Struct
onto an alternate code path that does not activate the bug.
Wrap the type in an intersection with Kernel
Simply switching the type from MyInterface[TypeArgument]
to T.all(MyInterface[TypeArgument], Kernel)
is also enough to nudge T::Struct
onto the code path listed above.
Disable runtime checks while running Tapioca
If you have some code that can detect when Tapioca is running and set your implementation of T::Configuration.call_validation_error_handler
to something that no-ops, you won't encounter the crash.