tapioca icon indicating copy to clipboard operation
tapioca copied to clipboard

Tapioca DSL crashes when using `T::Struct` containing generic members

Open amanda-mitchell opened this issue 1 year ago • 1 comments

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.

amanda-mitchell avatar Dec 20 '24 20:12 amanda-mitchell

Thanks for the very detailed issue and the workarounds, very useful for others who might experience this in the future. I think it'd be best if sorbet-runtime was modified to respect default_checked_level.

We don't use T::Struct internally so I don't imagine us getting around to this issue but contributions are welcome to both here and to the sorbet repo 🙂

KaanOzkan avatar Jan 20 '25 19:01 KaanOzkan