-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Syntactic impl
declaration matching updates
#4762
Merged
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
bd77cfa
Checkpoint progress.
josh11b 7502868
Checkpoint progress.
josh11b 8bf2b71
DumpIfValid -> DumpNameIfValid
josh11b f307afb
Remove extra newline
josh11b 8c7404a
Merge remote-tracking branch 'upstream/trunk' into dump
josh11b 415c186
Failing tests
josh11b 884821d
Debug
josh11b 0fe446e
Merge branch 'dump' into assoc
josh11b edb5c9f
Roughly working
josh11b a987574
Update tests
josh11b 9e0c236
Checkpoint progress.
josh11b f7bbc18
More diagnostics
josh11b 2e0014a
Add TODOs about broken syntactic impl match
josh11b ab99a06
Exclude `where` from syntactic impl match
josh11b adfdfe4
`impl Self as` now matches `impl as`
josh11b aff650a
Checkpoint progress.
josh11b 2285d25
Checkpoint progress.
josh11b 11abc91
add impl_self_as tests
josh11b eed17cd
Comment
josh11b 539474e
Checkpoint progress.
josh11b c7e2e18
Skip difference between `Self` and `Self as` in `CheckRedeclParamSynt…
josh11b a68b52d
TODO comments
josh11b ee6fa85
Checkpoint progress.
josh11b f25dda2
Merge remote-tracking branch 'upstream/trunk' into assoc
josh11b c7d82d6
Checkpoint progress.
josh11b e17d9f6
Checkpoint progress.
josh11b 4d1155d
Test updates
josh11b 95f98c1
Syntactic `impl` declaration matching updates
josh11b 694204e
Checkpoint progress.
josh11b 8afd387
Checkpoint progress.
josh11b 1580edb
Checkpoint progress.
josh11b 21c5af6
More tests
josh11b a320cbf
Implement suggestions
josh11b File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
This file contains 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
180 changes: 180 additions & 0 deletions
180
toolchain/check/testdata/impl/no_prelude/impl_self_as.carbon
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM | ||
// Exceptions. See /LICENSE for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
// EXTRA-ARGS: --no-dump-sem-ir | ||
// | ||
// AUTOUPDATE | ||
// TIP: To test this file alone, run: | ||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/no_prelude/impl_self_as.carbon | ||
// TIP: To dump output, run: | ||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/impl_self_as.carbon | ||
|
||
// --- match.carbon | ||
library "[[@TEST_NAME]]"; | ||
|
||
interface I1 {} | ||
interface I2 {} | ||
interface J1(T1:! type) {} | ||
interface J2(T2:! type) {} | ||
|
||
// `impl Self as` should match `impl as`, so these should not trigger impl | ||
// declaration without definition diagnostics. | ||
|
||
class C1 { | ||
impl Self as I1; | ||
impl as I1 {} | ||
|
||
impl as I2; | ||
impl Self as I2 {} | ||
|
||
impl forall [U:! type] Self as J1(U); | ||
impl forall [U:! type] as J1(U) {} | ||
|
||
impl forall [V:! type] as J2(V); | ||
impl forall [V:! type] Self as J2(V) {} | ||
} | ||
|
||
class C2(W:! type) { | ||
impl Self as I1; | ||
impl as I1 {} | ||
|
||
impl as I2; | ||
impl Self as I2 {} | ||
|
||
impl forall [X:! type] Self as J1(X); | ||
impl forall [X:! type] as J1(X) {} | ||
|
||
impl forall [Y:! type] as J2(Y); | ||
impl forall [Y:! type] Self as J2(Y) {} | ||
} | ||
|
||
|
||
// --- fail_no_match.carbon | ||
library "[[@TEST_NAME]]"; | ||
|
||
interface I3 {} | ||
interface I4 {} | ||
interface I5 {} | ||
interface I6 {} | ||
interface J3(T3:! type) {} | ||
interface J4(T4:! type) {} | ||
interface J5(T5:! type) {} | ||
interface J6(T6:! type) {} | ||
|
||
// `impl C as` should not match `impl Self as` or `impl as`. | ||
|
||
class C3 { | ||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl C3 as I3; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl C3 as I3; | ||
impl as I3 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl C3 as I4; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl C3 as I4; | ||
impl Self as I4 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl as I5; | ||
// CHECK:STDERR: ^~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl as I5; | ||
impl C3 as I5 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl Self as I6; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl Self as I6; | ||
impl C3 as I6 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [Z3:! type] C3 as J3(Z3); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [Z3:! type] C3 as J3(Z3); | ||
impl forall [Z3:! type] as J3(Z3) {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [Z4:! type] C3 as J4(Z4); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [Z4:! type] C3 as J4(Z4); | ||
impl forall [Z4:! type] Self as J4(Z4) {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [Z5:! type] as J5(Z5); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [Z5:! type] as J5(Z5); | ||
impl forall [Z5:! type] C3 as J5(Z5) {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [Z6:! type] Self as J6(Z6); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [Z6:! type] Self as J6(Z6); | ||
impl forall [Z6:! type] C3 as J6(Z6) {} | ||
} | ||
|
||
class C4(A:! type) { | ||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl C4(A) as I3; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl C4(A) as I3; | ||
impl as I3 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl C4(A) as I4; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl C4(A) as I4; | ||
impl Self as I4 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl as I5; | ||
// CHECK:STDERR: ^~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl as I5; | ||
impl C4(A) as I5 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl Self as I6; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl Self as I6; | ||
impl C4(A) as I6 {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [B3:! type] C4(A) as J3(B3); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [B3:! type] C4(A) as J3(B3); | ||
impl forall [B3:! type] as J3(B3) {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [B4:! type] C4(A) as J4(B4); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [B4:! type] C4(A) as J4(B4); | ||
impl forall [B4:! type] Self as J4(B4) {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+4]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [B5:! type] as J5(B5); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// CHECK:STDERR: | ||
impl forall [B5:! type] as J5(B5); | ||
impl forall [B5:! type] C4(A) as J5(B5) {} | ||
|
||
// CHECK:STDERR: fail_no_match.carbon:[[@LINE+3]]:3: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl forall [B6:! type] Self as J6(B6); | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
impl forall [B6:! type] Self as J6(B6); | ||
impl forall [B6:! type] C4(A) as J6(B6) {} | ||
} |
47 changes: 47 additions & 0 deletions
47
toolchain/check/testdata/impl/no_prelude/impl_where_redecl.carbon
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM | ||
// Exceptions. See /LICENSE for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
// EXTRA-ARGS: --no-dump-sem-ir | ||
// | ||
// AUTOUPDATE | ||
// TIP: To test this file alone, run: | ||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/no_prelude/impl_where_redecl.carbon | ||
// TIP: To dump output, run: | ||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/no_prelude/impl_where_redecl.carbon | ||
|
||
// --- match.carbon | ||
library "[[@TEST_NAME]]"; | ||
|
||
interface J {} | ||
|
||
// `impl` matching ignores the `where` clause. It should not get confused by | ||
// nested `where`, so the following should not trigger impl declaration without | ||
// definition diagnostics. | ||
|
||
impl () as J; | ||
impl () as J where .Self impls type and .Self impls (type where .Self impls type) {} | ||
|
||
impl {} as J where .Self impls type and .Self impls (type where .Self impls type); | ||
impl {} as J {} | ||
|
||
// --- parens_other_nesting.carbon | ||
library "[[@TEST_NAME]]"; | ||
|
||
interface K {} | ||
|
||
// `impl` matching only ignores a root-level `where` clause. | ||
|
||
impl {} as (K where .Self impls type) where .Self impls type; | ||
impl {} as (K where .Self impls type) {} | ||
|
||
// --- fail_other_nesting.carbon | ||
library "[[@TEST_NAME]]"; | ||
|
||
interface L {} | ||
|
||
// CHECK:STDERR: fail_other_nesting.carbon:[[@LINE+3]]:1: error: impl declared but not defined [MissingImplDefinition] | ||
// CHECK:STDERR: impl () as (L where .Self impls type) where .Self impls type; | ||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
impl () as (L where .Self impls type) where .Self impls type; | ||
impl () as L {} |
This file contains 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
This file contains 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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to defer doing this until we build the
DeclParams
objects inMergeImplRedecl
? That'd mean we don't need to do the scan here until we're about to compare two token sequences anyway, which would avoid doing this scan entirely in cases where the impl isn't in the same bucket as any other impl (which is probably a common case), and would likely increase locality given that we'd be doing two checks of nearby tokens at around the same time.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonmeow What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To try to offer a few upsides to the current approach:
But maybe I'd bring up a more radical option: in
WhereExpr
handling, if there's animpl
on the stack and it hasn't reached awhere
clause, had you considered trying to store thewhere
location? Maybe that could be annotated as part ofdecl_introducer_state_stack
. Could that give the result you want without the scan of nodes?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we could avoid the scan by doing saving something in the
WhereExpr
handling, but it seems really fragile. There are a lot of places where awhere
clause can occur in animpl
declaration that we don't want to truncate at.I think the current code is easier to get right since it directly looks in the one place we want to operate on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In each of the cases you're showing, it seems like there'd be something on the NodeStack indicating the nesting:
[
at[T:!
(
at(Container
(
at(K
(
at(... where
Maybe it's worth thinking about further?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put differently, is there a characteristic here where 2 nodes before the
where
must be something likeforall
oras
?