-
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
Disallow abstract
or base
on class declarations (that are not definitions)
#4378
Disallow abstract
or base
on class declarations (that are not definitions)
#4378
Conversation
Hmm, in the process of testing this further, I came across existing test coverage that verified a different rule (that the base/abstract specifiers had to match between redeclarations) and linked to a still-open issue: #3384 Looks like #3762 was merged in March 2024, #3384 was filed in Nov 2023, but @josh11b mentioned in #3384 (comment) that there was still ongoing discussion about how this should be addressed? So... I provide this patch as a foray into discussing how this is meant to work/what bits of the spec/approved proposals are authoritative with respect to answering this question. |
It looks like #3762 and that discussion ultimately agree that for |
return {.base = {.a = 1}, .d = 7}; | ||
} | ||
|
||
fn Access(d: Derived) -> (i32, i32) { | ||
return (d.d, d.base.a); | ||
} | ||
|
||
// CHECK:STDERR: fail_abstract.carbon:[[@LINE+3]]:1: error: `abstract` not allowed on `class` declaration |
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.
It would be nice (but not required) to have the error say this modifier should only be on the definition.
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.
Yeah, it crossed my mind when developing this, but the generic functionality for disallowed modifiers was rather convenient & adding this special case felt like it'd be pretty ugly.
I've implemented it/updated this patch with that change just for the exploration - maybe there's nicer ways to do it (with a callback?) or perhaps it feels worth the complexity/inelegance. Open to ideas/refactorings/revertings/renamings/etc.
@@ -8,6 +8,10 @@ | |||
// TIP: To dump output, run: | |||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_modifiers.carbon | |||
|
|||
// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+11]]:9: error: `abstract` not allowed on `class` declaration | |||
// CHECK:STDERR: private abstract private class DuplicatePrivate; |
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.
Feels better to have separate tests for "abstract not allowed on declaration" from "modifier repeated" and so on. Perhaps make these tests use a class definition ({}
instead of ;
), now that those are different? (similarly other tests)
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.
Agreed & have updated the test in that direction - though wasn't sure how much you were thinking we should go (like - revisiting existing untouched test cases into more distinct/isolated tests? Happy to do that, but haven't done it yet).
Update: #3384 has now been resolved. |
…initions) Per: https://docs.carbon-lang.dev/proposals/p3762.html#modifier-keywords:~:text=Other%20class%2C%20impl%2C%20and%20interface%20modifiers%20%28abstract%2C%20base%2C%20final%29%20exist%20only%20on%20the%20definition%2C%20not%20on%20the%20forward%20declaration "Other class, impl, and interface modifiers (`abstract`, `base`, `final`) exist only on the definition, not on the forward declaration."
Also add definitions to cases where the decl/def distinction isn't needed, and the decl case is invalid for other reasons that are tested separately.
762111f
to
964be4f
Compare
…tions, but not declarations
toolchain/check/handle_class.cpp
Outdated
auto accepted_modifiers = | ||
KeywordModifierSet::Access | KeywordModifierSet::Extern; | ||
auto invalid_unless_definition_modifiers = KeywordModifierSet::None; | ||
(is_definition ? accepted_modifiers : invalid_unless_definition_modifiers) |= | ||
KeywordModifierSet::Class; | ||
LimitModifiersOnDecl(context, introducer, accepted_modifiers, | ||
invalid_unless_definition_modifiers); |
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 find this a bit hard to read. I wonder if it would be simpler to introduce another function for this case? Maybe something like:
auto accepted_modifiers = | |
KeywordModifierSet::Access | KeywordModifierSet::Extern; | |
auto invalid_unless_definition_modifiers = KeywordModifierSet::None; | |
(is_definition ? accepted_modifiers : invalid_unless_definition_modifiers) |= | |
KeywordModifierSet::Class; | |
LimitModifiersOnDecl(context, introducer, accepted_modifiers, | |
invalid_unless_definition_modifiers); | |
LimitModifiersOnDecl(context, introducer, | |
KeywordModifierSet::Access | KeywordModifierSet::Extern | | |
KeywordModifierSet::Class); | |
if (!is_definition) { | |
LimitModifiersNotDefinition(context, introducer, | |
KeywordModifierSet::Access | KeywordModifierSet::Extern); | |
} |
The only difference between the two would be the diagnostic issued, so they could mostly share implementation.
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.
Thanks for the suggestion, yeah - that does simplify things greatly! (done)
// CHECK:STDERR: fail_abstract.carbon:[[@LINE+3]]:1: error: `abstract` not allowed on `class` declaration, only definition | ||
// CHECK:STDERR: abstract class AbstractDecl; | ||
// CHECK:STDERR: ^~~~~~~~ | ||
abstract class AbstractDecl; |
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.
This should be in another file split. The ideal here is that each should-fail test is in its own split, so accidentally succeeding is considered an error. In this case, the previous test should really be a "fail_todo" test, that is expected to succeed when the feature is implemented.
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.
Ah, thanks - sure, had a go at adding some splits.
// CHECK:STDERR: fail_base_bad_type.carbon:[[@LINE+4]]:1: error: `base` not allowed on `class` declaration, only definition | ||
// CHECK:STDERR: base class Incomplete; | ||
// CHECK:STDERR: ^~~~ | ||
// CHECK:STDERR: |
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.
Again a case where the tests should be in separate splits. Many of these files predate us supporting splits. This is also a case where "base
only allowed on definitions" should be separated from other tests about incomplete types.
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.
Made some splits here too.
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.
Thanks, particularly for cleaning up the tests you were touching!
Per: p3762: "Other class, impl, and interface modifiers (
abstract
,base
,final
) exist only on the definition, not on the forward declaration."