Repository navigation
Conversation
When a class defines its own singleton `[]` (for example to keep `Foo[Bar]` working after dropping `sorbet-runtime`'s `T::Generic#[]`) and its RBS signature mentions a generic application of that same class, such as `#: [U] (*Class[U]) -> Class[Foo[U]]`, the translated runtime sig calls `Foo.[]` while sorbet-runtime builds it. The wrapped `Foo.[]` must build its own sig before it runs, so evaluating the sig recurses until `SystemStackError`. Tapioca's RBS rewriter translates every typed file it loads, gems included, so `tapioca dsl` in any application that runs all sig blocks fails as soon as such a gem is loaded. Emit `T::Sig::WithoutRuntime.sig` for these signatures, the same way `method_added` and `singleton_method_added` are already handled. Sorbet still checks them statically; at runtime they are never wrapped or evaluated. Co-authored-by: Ufuk Kayserilioglu <ufuk.kayserilioglu@shopify.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
A class can define its own singleton
[]so thatFoo[Bar]keeps working at runtime withoutT::Generic, for example after a gem drops itssorbet-runtimedependency and moves its signatures to RBS comments:Translated to a runtime sig, that return type calls
ClientsFactory.[]while sorbet-runtime builds the sig. The wrappedClientsFactory.[]has to build its own sig before it runs, so evaluating the sig recurses untilSystemStackError. Tapioca's RBS rewriter translates every typed file it loads, gems included, sotapioca dslfails in any application that runs all sig blocks as soon as such a gem is loaded. We hit this with a gem bump in a large Rails app: the stack overflowed inT::Private::Methods.run_sig_block_for_keywith the gem'sClientsFactorysig block on every frame.Implementation
RBSCommentsToSorbetSigsnow tracks the class/module nesting while it visits the file. When it translates a signature for a singleton[](def self.[], ordef []insideclass << self) and that signature contains a generic application of the enclosing constant (Foo[...]or a qualifiedA::Foo[...], anywhere in a param or return type), it marks the sigwithout_runtime. This works the same waymethod_addedandsingleton_method_addedalready do. Sorbet still checks the signature statically; at runtime it is never wrapped or evaluated.The rule is deliberately narrow: a sig on any other method that mentions
Foo[...], an instance[], a singleton[]returning another class's generic, and#: (*untyped) -> selfall keep their runtime sigs. A[]defined in another file, or insideclass << Other, is not detected.Tests
test_translate_to_rbi_singleton_bracket_applying_own_generic_is_without_runtimecovers bothclass << selfanddef self.[]forms, an unqualified and a qualified self-reference, in both output formats. It fails without the change.test_translate_to_rbi_keeps_runtime_sigs_that_do_not_call_their_own_bracket_methodcovers the cases above that must keep runtime sigs.T::Utils.run_all_sig_blockssucceeds, andClientsFactory[Object]still returns the class. Withmain, the same script raisesSystemStackError.srb tcand RuboCop are clean on the changed files. The full suite gives the same results with and without this change (712 → 714 tests, the 2 new ones pass). The 143 failures and errors on both sides are CLI subprocess tests that can't find their gems in the environment I ran them in.