Skip to content

Fix #21721: make case TypeBlock(_, _) not match non-type Block #21722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 4, 2025

Conversation

felher
Copy link
Contributor

@felher felher commented Oct 7, 2024

TypeBlocks are represented as normal Blocks in the Quotes API's implementation. The current TypeTest for TypeBlock is exactly the same as the TypeTest for Block, which means that case TypeBlock(_, _) "matches" every block.

The implementation of unapply on TypeBlockModule, however, gives back (List[TypeDef], TypeTree). It constructs the List[TypeDef] by mapping over every statement of the block, trying to turn it into a TypeDef by using a match with the pattern

  case alias: TypeDef => alias

This seems fine since TypeBlocks are supposed to be just a list of TypeDefs followed by a type as the last expression. Since the TypeTest matches any Block and not only Blocks that are TypeBlocks, the statements can be anything, not just TypeDefs, which lets the whole case TypeBlock(_, _) pattern die with a MatchError.

This commit fixes the problem by making the TypeTest check whether the Block is a type (which in turns checks whether the Blocks expression is a type)

This is my first PR, please let me know if I should change anything or am completely going about fixing this the wrong way, etc.

Also, I could not run the complete test suite on my machine, it died even without making any changes. But I could at least run the positive compilation tests.

Closes #21721

TypeBlocks are represented as normal Blocks in the Quotes API. The
current TypeTest for TypeBlock is exactly the same as the TypeTest for
Block, which means that case TypeBlock(_, _) matches every block.

The implementation of unapply on TypeBlockModule, however, gives back
(List[TypeDef], TypeTree). It constructs the List[TypeDef] by mapping
over every statement, turning it into a TypeDef by using a match
with the pattern

  case alias: TypeDef => alias

Since the TypeTest matches any Block and not only Blocks that are
TypeBlocks, the statemnts can be anything, not just TypeDefs, which lets
the whole case TypeBlock(_, _) pattern fail with a MatchError.

This commit fixes the problem by making the TypeTest check whether the
Block is a type (which in turns checks whether the blocks expression is
a type)
@felher
Copy link
Contributor Author

felher commented Oct 7, 2024

I'm a bit unsure whether the failure of the CI has anything to do with the change proposed in this PR.

On my machine, scala3-bootstrapped/testOnly dotty.tools.dotc.CompilationTests runs through fine. No problem like

Error:  Test dotty.tools.dotc.CompilationTests.runAll failed: java.lang.AssertionError: Run test failed, but should not, reasons:
Error:  encountered 1 test failure(s):
Error:    - test 'tests\run\lambda-serialization-gc.scala' timed out, took 179.28 sec
Error:      at dotty.tools.vulpix.ParallelTesting$CompilationTest.checkPass(ParallelTesting.scala:1257)
Error:      at dotty.tools.vulpix.ParallelTesting$CompilationTest.checkRuns(ParallelTesting.scala:1221)
Error:      at dotty.tools.dotc.CompilationTests.runAll(CompilationTests.scala:168)
Error:      ...

I got:

[info] Test dotty.tools.dotc.CompilationTests.runAll started
[=======================================>] completed (1806/1806, 0 failed, 94s)

@hamzaremmal
Copy link
Member

Hi @felher, the CI failure has nothing to do with the proposed change. I've restarted the failed job.

Copy link
Contributor

@jchyb jchyb left a comment

Choose a reason for hiding this comment

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

Thank you for submitting this PR, the changes look good. The underlying isType checks if the Block result is a TypeTree, which is only supposed to happen for TypeBlocks, which makes this added guard sufficient.

Apologies for the very long wait - I must have missed this one initially. After some testing, we might also need to add an opposite check for regular Blocks, since now TypeBlocks can be matched into Blocks while containing different types, resulting in similar creashes, but all of this can be done separately.

@jchyb jchyb merged commit a97974c into scala:main Mar 4, 2025
28 checks passed
hamzaremmal added a commit that referenced this pull request Mar 5, 2025
Part 2 to #21722 
We do the same thing as there, but this time to make sure TypeBlocks are
not matched as Blocks. The tests from that PR were also moved, as I did
not realize they were put in `tests/pos` instead of `tests/pos-macros`
when making the review.
@hamzaremmal
Copy link
Member

FYI: I believe there is an issue with this PR. I've summarized the problem and shared it with @jchyb.

@jchyb
Copy link
Contributor

jchyb commented Mar 5, 2025

The problem @hamzaremmal mentioned is that in the reflection api docs we specify that all of the statements of a TypeBlock are supposed to be TypeDefs, but we do not check that here…. I want to refute this point. As I mentioned in my review comment:

The underlying isType checks if the Block result is a TypeTree, which is only supposed to happen for TypeBlocks, which makes this added guard sufficient.

It is impossible in the reflection API to create a regular Block with a TypeTree expr field, so none of that would be matched against TypeBlock. TypeBlock apply however only allows using TypeDefs in place of statements (or, for TypeBlock, aliases). Looking at the types of both, the isType check (which internally actually checks if the expr field is a TypeTree) is sufficient, as it's impossible to create a tpd.Block with non-TypeDef statements and TypeTree result using the reflection API.

About TypeBlocks generated by the compiler (users cannot, and were never able to create them themselves by code)… I actually were not able to get any in a macro. Currently, I believe only parser can create those when parsing a quoted type pattern (like '[type B; List[B]), but by the time a transparent inline macro is expanded, those are already desugared into a different tree). We might want to think about deprecating the TypeBlock methods in the future.

@WojciechMazur WojciechMazur added this to the 3.7.0 milestone Mar 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TypeTest of TypeBlock of Quotes API matches every Block
4 participants