Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions docs/design/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

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.

`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
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
| 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_ |
| 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 />`override`<br />_not mentioned_ |

| `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` |
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
| `override` |||| `virtual`<br />`abstract`<br />`override` | `abstract`<br />`override` |
| `override` |||| `virtual`<br />`abstract`<br />`override` | `abstract`<br />`override` |


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
Expand Down Expand Up @@ -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;
}
```

Expand All @@ -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*`.
}
Expand Down Expand Up @@ -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 {
Expand All @@ -1624,7 +1625,7 @@ base class MyBaseClass {

class MyDerivedClass {
extend base: MyBaseClass;
impl fn destroy[addr self: Self*]() { ... }
override fn destroy[addr self: Self*]() { ... }
}
```

Expand Down
91 changes: 91 additions & 0 deletions proposals/p5711.md
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)

Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
- [Keep `impl fn`](#keep-impl-fn)
- [Use different keywords](#use-different-keywords)

<!-- tocstop -->

## Abstract

Change the keyword for method overriding from `impl fn` to `override fn` in class inheritance.
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
Change the keyword for method overriding from `impl fn` to `override fn` in class inheritance.
Change the keyword for method overriding from `impl fn` to `override fn` in
class inheritance.


## 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
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.
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.


## 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:
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
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:
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:


1. **Interface implementation**: `impl SomeInterface for SomeType`
2. **Method overriding**: `impl fn SomeMethod()` (current) vs `override fn SomeMethod()` (proposed)
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
2. **Method overriding**: `impl fn SomeMethod()` (current) vs `override fn SomeMethod()` (proposed)
2. **Method overriding**: `impl fn SomeMethod()` (current) vs
`override fn SomeMethod()` (proposed)


## Proposal

Replace all uses of `impl fn` with `override fn` for method overriding in class inheritance:
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
Replace all uses of `impl fn` with `override fn` for method overriding in class inheritance:
Replace all uses of `impl fn` with `override fn` for method overriding in class
inheritance:


**Before:**
```carbon
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
```carbon
```carbon

base class Shape {
virtual fn Draw[self: Self]();
}
class Circle extends Shape {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
class Circle extends Shape {
class Circle {
extend base: Shape;

impl fn Draw[self: Self]() {
// Implementation
}
}
```

**After:**
```carbon
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
```carbon
```carbon

base class Shape {
virtual fn Draw[self: Self]();
}
class Circle extends Shape {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
class Circle extends Shape {
class Circle {
extend base: Shape;

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
1. **Clarity**: `override` makes it immediately clear that the method is overriding a parent method, not implementing an interface.
1. **Clarity**: `override` makes it immediately clear that the method is
overriding a parent method, not implementing an interface.


2. **Familiarity**: C++ developers are already familiar with the `override` keyword, reducing the learning curve.
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
2. **Familiarity**: C++ developers are already familiar with the `override`
keyword, reducing the learning curve.


3. **Consistency**: Many other languages (C++, C#, Java) use `override` for method overriding, making Carbon more consistent with established patterns.
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
3. **Consistency**: Many other languages (C++, C#, Java) use `override` for
method overriding, making Carbon more consistent with established patterns.


4. **Separation of concerns**: Clear separation between interface implementation (`impl`) and method overriding (`override`).
Copy link
Contributor

Choose a reason for hiding this comment

The 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`).
4. **Separation of concerns**: Clear separation between interface implementation
(`impl`) and method overriding (`override`).


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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
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.


## Alternatives considered

### Keep `impl fn`

We could maintain the status quo, but this creates ongoing confusion between interface implementation and method overriding.
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
We could maintain the status quo, but this creates ongoing confusion between interface implementation and method overriding.
We could maintain the status quo, but this creates ongoing confusion between
interface implementation and method overriding.


### 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
Other keywords like `redefine` or `specialize` could be used, but `override` is already well-established in the programming community and clearly conveys the intent.
Other keywords like `redefine` or `specialize` could be used, but `override` is
already well-established in the programming community and clearly conveys the
intent.

4 changes: 2 additions & 2 deletions toolchain/check/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Override) {
if (fn.virtual_modifier !=
SemIR::FunctionFields::VirtualModifier::Override) {

fn.virtual_index = vtable.size();
vtable.push_back(inst_id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ base class C {
base class D {
extend base: C;
var value_d: i32;
impl fn Foo[self: Self]() -> i32 {
override fn Foo[self: Self]() -> i32 {
return self.value_d;
}
}

class E {
extend base: D;
var value_e: i32;
impl fn Foo[self: Self]() -> i32 {
override fn Foo[self: Self]() -> i32 {
return self.value_e;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ base class C {

class D {
extend base: C;
impl fn Foo[self: Self]() -> i32 {
override fn Foo[self: Self]() -> i32 {
return 3;
}
fn Bar[self: Self]() -> i32 {
Expand Down
6 changes: 3 additions & 3 deletions toolchain/check/handle_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
.Case(KeywordModifierSet::Override, SemIR::Function::VirtualModifier::Override)
.Case(KeywordModifierSet::Override,
SemIR::Function::VirtualModifier::Override)

.Default(SemIR::Function::VirtualModifier::None);
}

Expand Down Expand Up @@ -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");
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "override without base class");
CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "override without base class");

context.emitter().Emit(node_id, ImplWithoutBase);
}

Expand Down
15 changes: 15 additions & 0 deletions toolchain/check/import_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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] =
Copy link
Contributor

Choose a reason for hiding this comment

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

[diff] reported by reviewdog 🐶

Suggested change
auto [pointee_type_inst_id, pointee_type_id] =
auto [pointee_type_inst_id, pointee_type_id] =

MapType(context, loc_id, pointee_type);

Copy link
Contributor

Choose a reason for hiding this comment

The 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};
}

Copy link
Contributor

Choose a reason for hiding this comment

The 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};
}
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/keyword_modifier_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ class KeywordModifierSet {
Default = 1 << 6,
Export = 1 << 7,
Final = 1 << 8,
Impl = 1 << 9,
Override = 1 << 9,
Virtual = 1 << 10,
Returned = 1 << 11,

// Sets of modifiers:
Access = Private | Protected,
Class = Abstract | Base,
Method = Abstract | Impl | Virtual,
Method = Abstract | Override | Virtual,
ImplDecl = Extend | Final,
Interface = Default | Final,
Decl = Class | Method | Interface | Export | Returned,
Expand Down
Loading
Loading