-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Replace impl fn with override fn #5776
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1237,21 +1237,22 @@ There are three virtual modifier keywords: | |||||||||||||||||||
virtual" but is called | ||||||||||||||||||||
["pure virtual" in C++](https://en.wikipedia.org/wiki/Virtual_function#Abstract_classes_and_pure_virtual_functions). | ||||||||||||||||||||
Only abstract classes may have unimplemented abstract methods. | ||||||||||||||||||||
- `impl` - This marks a method that overrides a method marked `virtual` or | ||||||||||||||||||||
- `override` - This marks a method that overrides a method marked `virtual` or | ||||||||||||||||||||
`abstract` in the base class with an implementation specific to -- and | ||||||||||||||||||||
defined within -- this class. The method is still virtual and may be | ||||||||||||||||||||
overridden again in subsequent derived classes if this is a base class. See | ||||||||||||||||||||
[method overriding in Wikipedia](https://en.wikipedia.org/wiki/Method_overriding). | ||||||||||||||||||||
Requiring a keyword when overriding allows the compiler to diagnose when the | ||||||||||||||||||||
derived class accidentally uses the wrong signature or spelling and so | ||||||||||||||||||||
doesn't match the base class. We intentionally use the same keyword here as | ||||||||||||||||||||
for implementing interfaces, to emphasize that they are similar operations. | ||||||||||||||||||||
doesn't match the base class. This keyword provides clear separation from | ||||||||||||||||||||
interface implementation and follows established conventions from languages | ||||||||||||||||||||
like C++. | ||||||||||||||||||||
|
||||||||||||||||||||
| Keyword on<br />method in `C` | Allowed in<br />`abstract class C` | Allowed in<br />`base class C` | Allowed in<br />final `class C` | in `B` where<br />`C` extends `B` | in `D` where<br />`D` extends `C` | | ||||||||||||||||||||
| ----------------------------- | ---------------------------------- | ------------------------------ | ------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------- | | ||||||||||||||||||||
| `virtual` | ✅ | ✅ | ❌ | _not present_ | `abstract`<br />`impl`<br />_not mentioned_ | | ||||||||||||||||||||
| `abstract` | ✅ | ❌ | ❌ | _not present_<br />`virtual`<br />`abstract`<br />`impl` | `abstract`<br />`impl`<br />_may not be<br />mentioned if<br />`D` is not final_ | | ||||||||||||||||||||
| `impl` | ✅ | ✅ | ✅ | `virtual`<br />`abstract`<br />`impl` | `abstract`<br />`impl` | | ||||||||||||||||||||
| `virtual` | ✅ | ✅ | ❌ | _not present_ | `abstract`<br />`override`<br />_not mentioned_ | | ||||||||||||||||||||
Comment on lines
1251
to
+1253
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||||||||||
| `abstract` | ✅ | ❌ | ❌ | _not present_<br />`virtual`<br />`abstract`<br />`override` | `abstract`<br />`override`<br />_may not be<br />mentioned if<br />`D` is not final_ | | ||||||||||||||||||||
| `override` | ✅ | ✅ | ✅ | `virtual`<br />`abstract`<br />`override` | `abstract`<br />`override` | | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||||||||||
|
||||||||||||||||||||
Since validating a method with a virtual modifier keyword involves looking for | ||||||||||||||||||||
methods with the same name in the base class, virtual methods must be declared | ||||||||||||||||||||
|
@@ -1315,15 +1316,15 @@ base class B1 { | |||||||||||||||||||
class D1 { | ||||||||||||||||||||
extend base: B1; | ||||||||||||||||||||
// ❌ Illegal: | ||||||||||||||||||||
// impl fn F[self: Self](x: Self) -> Self; | ||||||||||||||||||||
// override fn F[self: Self](x: Self) -> Self; | ||||||||||||||||||||
// since that would mean the same thing as: | ||||||||||||||||||||
// impl fn F[self: Self](x: D1) -> D1; | ||||||||||||||||||||
// override fn F[self: Self](x: D1) -> D1; | ||||||||||||||||||||
// and `D1` is a different type than `B1`. | ||||||||||||||||||||
|
||||||||||||||||||||
// ✅ Allowed: Parameter and return types | ||||||||||||||||||||
// of `F` match declaration in `B1`. | ||||||||||||||||||||
impl fn F[self: Self](x: B1) -> B1; | ||||||||||||||||||||
// Or: impl fn F[self: D1](x: B1) -> B1; | ||||||||||||||||||||
override fn F[self: Self](x: B1) -> B1; | ||||||||||||||||||||
// Or: override fn F[self: D1](x: B1) -> B1; | ||||||||||||||||||||
} | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
|
@@ -1341,9 +1342,9 @@ base class B2 { | |||||||||||||||||||
class D2 { | ||||||||||||||||||||
extend base: B2; | ||||||||||||||||||||
// ✅ Allowed | ||||||||||||||||||||
impl fn Clone[self: Self]() -> Self*; | ||||||||||||||||||||
override fn Clone[self: Self]() -> Self*; | ||||||||||||||||||||
// Means the same thing as: | ||||||||||||||||||||
// impl fn Clone[self: D2]() -> D2*; | ||||||||||||||||||||
// override fn Clone[self: D2]() -> D2*; | ||||||||||||||||||||
// which is allowed since `D2*` is a | ||||||||||||||||||||
// subtype of `B2*`. | ||||||||||||||||||||
} | ||||||||||||||||||||
|
@@ -1615,7 +1616,7 @@ of its base classes unless it has a | |||||||||||||||||||
[virtual destructor](https://en.wikipedia.org/wiki/Virtual_function#Virtual_destructors). | ||||||||||||||||||||
An abstract or base class' destructor may be declared virtual using the | ||||||||||||||||||||
`virtual` introducer, in which case any derived class destructor declaration | ||||||||||||||||||||
must be `impl`: | ||||||||||||||||||||
must be `override`: | ||||||||||||||||||||
|
||||||||||||||||||||
```carbon | ||||||||||||||||||||
base class MyBaseClass { | ||||||||||||||||||||
|
@@ -1624,7 +1625,7 @@ base class MyBaseClass { | |||||||||||||||||||
|
||||||||||||||||||||
class MyDerivedClass { | ||||||||||||||||||||
extend base: MyBaseClass; | ||||||||||||||||||||
impl fn destroy[addr self: Self*]() { ... } | ||||||||||||||||||||
override fn destroy[addr self: Self*]() { ... } | ||||||||||||||||||||
} | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,91 @@ | ||||||||||||
# Replace `impl fn` with `override fn` | ||||||||||||
|
||||||||||||
<!-- | ||||||||||||
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 | ||||||||||||
--> | ||||||||||||
|
||||||||||||
[Issue #5711](https://github.com/carbon-language/carbon-lang/issues/5711) | ||||||||||||
|
||||||||||||
<!-- toc --> | ||||||||||||
|
||||||||||||
## Table of contents | ||||||||||||
|
||||||||||||
- [Abstract](#abstract) | ||||||||||||
- [Problem](#problem) | ||||||||||||
- [Background](#background) | ||||||||||||
- [Proposal](#proposal) | ||||||||||||
- [Rationale](#rationale) | ||||||||||||
- [Alternatives considered](#alternatives-considered) | ||||||||||||
|
||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
<!-- tocstop --> | ||||||||||||
|
||||||||||||
## Abstract | ||||||||||||
|
||||||||||||
Change the keyword for method overriding from `impl fn` to `override fn` in class inheritance. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
## Problem | ||||||||||||
|
||||||||||||
The current use of `impl fn` for method overriding in class inheritance creates ambiguity with interface implementation and is less familiar to C++ developers. The keyword `impl` is heavily used for implementing interfaces, making the distinction between interface implementation and method overriding less clear. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
## Background | ||||||||||||
|
||||||||||||
The original choice of `impl` was made in [proposal #777](https://github.com/carbon-language/carbon-lang/pull/777) to "draw a parallel with implementing interfaces." However, this creates confusion between two different concepts: | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
1. **Interface implementation**: `impl SomeInterface for SomeType` | ||||||||||||
2. **Method overriding**: `impl fn SomeMethod()` (current) vs `override fn SomeMethod()` (proposed) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
## Proposal | ||||||||||||
|
||||||||||||
Replace all uses of `impl fn` with `override fn` for method overriding in class inheritance: | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
**Before:** | ||||||||||||
```carbon | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
base class Shape { | ||||||||||||
virtual fn Draw[self: Self](); | ||||||||||||
} | ||||||||||||
class Circle extends Shape { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
impl fn Draw[self: Self]() { | ||||||||||||
// Implementation | ||||||||||||
} | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
**After:** | ||||||||||||
```carbon | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
base class Shape { | ||||||||||||
virtual fn Draw[self: Self](); | ||||||||||||
} | ||||||||||||
class Circle extends Shape { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
override fn Draw[self: Self]() { | ||||||||||||
// Implementation | ||||||||||||
} | ||||||||||||
} | ||||||||||||
``` | ||||||||||||
|
||||||||||||
## Rationale | ||||||||||||
|
||||||||||||
This change improves Carbon in several ways: | ||||||||||||
|
||||||||||||
1. **Clarity**: `override` makes it immediately clear that the method is overriding a parent method, not implementing an interface. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
2. **Familiarity**: C++ developers are already familiar with the `override` keyword, reducing the learning curve. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
3. **Consistency**: Many other languages (C++, C#, Java) use `override` for method overriding, making Carbon more consistent with established patterns. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
4. **Separation of concerns**: Clear separation between interface implementation (`impl`) and method overriding (`override`). | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
5. **Addresses original concerns**: The original concern about `override` not matching abstract methods is resolved by understanding that `override` means "providing an implementation for a method declared in a parent class," whether that parent method was abstract or virtual. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
## Alternatives considered | ||||||||||||
|
||||||||||||
### Keep `impl fn` | ||||||||||||
|
||||||||||||
We could maintain the status quo, but this creates ongoing confusion between interface implementation and method overriding. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||||||
|
||||||||||||
### Use different keywords | ||||||||||||
|
||||||||||||
Other keywords like `redefine` or `specialize` could be used, but `override` is already well-established in the programming community and clearly conveys the intent. | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -150,7 +150,7 @@ static auto BuildVtable(Context& context, SemIR::ClassId class_id, | |||||||
auto& override_fn = | ||||||||
context.functions().Get(override_fn_decl.function_id); | ||||||||
if (override_fn.virtual_modifier == | ||||||||
SemIR::FunctionFields::VirtualModifier::Impl && | ||||||||
SemIR::FunctionFields::VirtualModifier::Override && | ||||||||
override_fn.name_id == fn.name_id) { | ||||||||
// TODO: Support generic base classes, rather than passing | ||||||||
// `SpecificId::None`. | ||||||||
|
@@ -171,7 +171,7 @@ static auto BuildVtable(Context& context, SemIR::ClassId class_id, | |||||||
for (auto inst_id : vtable_contents) { | ||||||||
auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(inst_id); | ||||||||
auto& fn = context.functions().Get(fn_decl.function_id); | ||||||||
if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Impl) { | ||||||||
if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Override) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||
fn.virtual_index = vtable.size(); | ||||||||
vtable.push_back(inst_id); | ||||||||
} | ||||||||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -128,7 +128,7 @@ static auto GetVirtualModifier(const KeywordModifierSet& modifier_set) | |||||||
SemIR::Function::VirtualModifier::Virtual) | ||||||||
.Case(KeywordModifierSet::Abstract, | ||||||||
SemIR::Function::VirtualModifier::Abstract) | ||||||||
.Case(KeywordModifierSet::Impl, SemIR::Function::VirtualModifier::Impl) | ||||||||
.Case(KeywordModifierSet::Override, SemIR::Function::VirtualModifier::Override) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||
.Default(SemIR::Function::VirtualModifier::None); | ||||||||
} | ||||||||
|
||||||||
|
@@ -342,9 +342,9 @@ static auto RequestVtableIfVirtual( | |||||||
} | ||||||||
|
||||||||
auto& class_info = context.classes().Get(class_decl->class_id); | ||||||||
if (virtual_modifier == SemIR::Function::VirtualModifier::Impl && | ||||||||
if (virtual_modifier == SemIR::Function::VirtualModifier::Override && | ||||||||
!class_info.base_id.has_value()) { | ||||||||
CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class"); | ||||||||
CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "override without base class"); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||||
context.emitter().Emit(node_id, ImplWithoutBase); | ||||||||
} | ||||||||
|
||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -568,6 +568,21 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type) | |||||
return MapRecordType(context, loc_id, *record_type); | ||||||
} | ||||||
|
||||||
if (const auto* pointer_type = clang::dyn_cast<clang::PointerType>(type)) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The changes in this file look like they're not related to the proposal; please can you split them out into a separate pull request? |
||||||
clang::QualType pointee_type = pointer_type->getPointeeType(); | ||||||
auto [pointee_type_inst_id, pointee_type_id] = | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||
MapType(context, loc_id, pointee_type); | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||
if (pointee_type_id == SemIR::ErrorInst::TypeId) { | ||||||
return {.inst_id = SemIR::ErrorInst::TypeInstId, | ||||||
.type_id = SemIR::ErrorInst::TypeId}; | ||||||
} | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [diff] reported by reviewdog 🐶
Suggested change
|
||||||
auto pointer_type_id = GetPointerType(context, pointee_type_inst_id); | ||||||
return {.inst_id = context.types().GetInstId(pointer_type_id), | ||||||
.type_id = pointer_type_id}; | ||||||
} | ||||||
|
||||||
return {.inst_id = SemIR::ErrorInst::TypeInstId, | ||||||
.type_id = SemIR::ErrorInst::TypeId}; | ||||||
} | ||||||
|
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.
Please also add links to the proposal document to "Alternatives considered" and "References" at the end of this file, following the existing pattern there.