Browse Source

Replace `impl fn` with `override fn` (#6008)

This proposal renames the syntax used to mark an overriding definition
of a virtual method from `impl fn` to `override fn` to avoid ambiguity:
besides indicating an overriding virtual function, it can be parsed as
an "impl" declaration when the construct following "impl" begins with a
lambda introduced by "fn".

Closes #5711

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Elliott Kalt 7 tháng trước cách đây
mục cha
commit
f4bd6e42f9

+ 17 - 16
docs/design/classes.md

@@ -1237,21 +1237,20 @@ 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.
 
-| 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`                                                           |
+| 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`                                                           |
 
 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 +1314,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 +1340,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 +1614,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 +1623,7 @@ base class MyBaseClass {
 
 class MyDerivedClass {
   extend base: MyBaseClass;
-  impl fn destroy[addr self: Self*]() { ... }
+  override fn destroy[addr self: Self*]() { ... }
 }
 ```
 
@@ -2298,6 +2297,8 @@ the type of `U.x`."
     -   [Destructor syntax options](/proposals/p5017.md#destructor-syntax-options)
     -   [Destructor name options](/proposals/p5017.md#destructor-name-options)
 
+-   [#6008: Replace `impl fn` with `override fn`](https://github.com/carbon-language/carbon-lang/pull/6008)
+
 ## References
 
 -   [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257)

+ 57 - 0
proposals/p6008.md

@@ -0,0 +1,57 @@
+# 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
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/6008)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Problem](#problem)
+-   [Background](#background)
+-   [Proposal](#proposal)
+-   [Rationale](#rationale)
+
+<!-- tocstop -->
+
+## Abstract
+
+This proposal renames the syntax used to mark an overriding definition of a
+virtual method from `impl fn` to `override fn` to avoid ambiguity.
+
+## Problem
+
+The phrase `impl fn` was introduced to mark overriding virtual functions,
+however, it is now ambiguous: besides indicating an overriding virtual function,
+it can be parsed as an "impl" declaration when the construct following "impl"
+begins with a lambda introduced by "fn".
+
+## Background
+
+The original syntax was adopted in
+[proposal #777](https://github.com/carbon-language/carbon-lang/pull/777) to
+emphasize that implementing interfaces and overriding virtual functions are
+similar operations.
+
+## Proposal
+
+This proposal is to replace the `impl fn` syntax with `override fn` for method
+overriding in class inheritance.
+
+Note that `impl` is still a modifier keyword for library and package
+declarations (`impl library ...` and `impl package ...` respectively). The
+former is unambiguous and the latter is easy to disambiguate, so no change is
+needed for these two cases.
+
+## Rationale
+
+This proposal is focused on the
+[That code is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
+Carbon goal. Changing the keyword from `impl` to `override` makes the intent
+clearer, resolves syntax ambiguity, and adheres to existing C++ conventions.

+ 7 - 5
toolchain/check/class.cpp

@@ -199,7 +199,7 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id,
                     .GetAs<SemIR::FunctionDecl>(override_fn_decl_id)
                     .function_id);
             return override_fn.virtual_modifier ==
-                       SemIR::FunctionFields::VirtualModifier::Impl &&
+                       SemIR::FunctionFields::VirtualModifier::Override &&
                    override_fn.name_id == fn.name_id;
           });
       if (i != vtable_contents.end()) {
@@ -237,13 +237,15 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_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) {
       fn.virtual_index = vtable.size();
       vtable.push_back(build_specific_function(inst_id));
     } else if (!implemented_impls.Lookup(fn_decl.function_id)) {
-      CARBON_DIAGNOSTIC(ImplWithoutVirtualInBase, Error,
-                        "impl without compatible virtual in base class");
-      context.emitter().Emit(SemIR::LocId(inst_id), ImplWithoutVirtualInBase);
+      CARBON_DIAGNOSTIC(OverrideWithoutVirtualInBase, Error,
+                        "override without compatible virtual in base class");
+      context.emitter().Emit(SemIR::LocId(inst_id),
+                             OverrideWithoutVirtualInBase);
     }
   }
 

+ 6 - 4
toolchain/check/handle_function.cpp

@@ -128,7 +128,8 @@ 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)
       .Default(SemIR::Function::VirtualModifier::None);
 }
 
@@ -342,10 +343,11 @@ 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");
-    context.emitter().Emit(node_id, ImplWithoutBase);
+    CARBON_DIAGNOSTIC(OverrideWithoutBase, Error,
+                      "override without base class");
+    context.emitter().Emit(node_id, OverrideWithoutBase);
     virtual_modifier = SemIR::Function::VirtualModifier::None;
     return;
   }

+ 3 - 2
toolchain/check/keyword_modifier_set.h

@@ -38,6 +38,7 @@ enum class ModifierOrder : int8_t { Access, Extern, Extend, Decl, Last = Decl };
   X(Export)                                                                  \
   X(Final)                                                                   \
   X(Impl)                                                                    \
+  X(Override)                                                                \
   X(Returned)                                                                \
   X(Virtual)
 
@@ -113,11 +114,11 @@ CARBON_KEYWORD_MODIFIER_SET(CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE)
 
 constexpr KeywordModifierSet KeywordModifierSet::Access(Private | Protected);
 constexpr KeywordModifierSet KeywordModifierSet::Class(Abstract | Base);
-constexpr KeywordModifierSet KeywordModifierSet::Method(Abstract | Impl |
+constexpr KeywordModifierSet KeywordModifierSet::Method(Abstract | Override |
                                                         Virtual);
 constexpr KeywordModifierSet KeywordModifierSet::ImplDecl(Extend | Final);
 constexpr KeywordModifierSet KeywordModifierSet::Interface(Default | Final);
-constexpr KeywordModifierSet KeywordModifierSet::Decl(Class | Method |
+constexpr KeywordModifierSet KeywordModifierSet::Decl(Class | Method | Impl |
                                                       Interface | Export |
                                                       Returned);
 

+ 109 - 109
toolchain/check/testdata/class/virtual_modifiers.carbon

@@ -34,7 +34,7 @@ import Modifiers;
 
 class Derived {
   extend base: Modifiers.Base;
-  impl fn H[self: Self]();
+  override fn H[self: Self]();
 }
 
 fn Use() {
@@ -72,7 +72,7 @@ abstract class A1 {
 
 abstract class A2 {
   extend base: A1;
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 // --- impl_base.carbon
@@ -85,12 +85,12 @@ base class B1 {
 
 base class B2 {
   extend base: B1;
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 class C {
   extend base: B2;
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 fn Use() {
@@ -104,11 +104,11 @@ fn Use() {
 library "[[@TEST_NAME]]";
 
 class C {
-  // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:3: error: impl without base class [ImplWithoutBase]
-  // CHECK:STDERR:   impl fn F[self: Self]();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:3: error: override without base class [OverrideWithoutBase]
+  // CHECK:STDERR:   override fn F[self: Self]();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 // --- init_members.carbon
@@ -139,11 +139,11 @@ base class Base {
 
 class Derived {
   extend base: Base;
-  // CHECK:STDERR: fail_impl_without_base_declaration.carbon:[[@LINE+4]]:3: error: impl without compatible virtual in base class [ImplWithoutVirtualInBase]
-  // CHECK:STDERR:   impl fn F[self: Self]();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_impl_without_base_declaration.carbon:[[@LINE+4]]:3: error: override without compatible virtual in base class [OverrideWithoutVirtualInBase]
+  // CHECK:STDERR:   override fn F[self: Self]();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 // --- abstract_impl.carbon
@@ -160,7 +160,7 @@ abstract class AbstractIntermediate {
 
 class Derived {
   extend base: AbstractIntermediate;
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 // --- virtual_impl.carbon
@@ -177,7 +177,7 @@ base class VirtualIntermediate {
 
 class Derived {
   extend base: VirtualIntermediate;
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 // --- fail_impl_mismatch.carbon
@@ -191,13 +191,13 @@ base class Base {
 class Derived {
   extend base: Base;
   // CHECK:STDERR: fail_impl_mismatch.carbon:[[@LINE+7]]:3: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers]
-  // CHECK:STDERR:   impl fn F[self: Self](v: i32);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:   override fn F[self: Self](v: i32);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_impl_mismatch.carbon:[[@LINE-8]]:3: note: previously declared with parameter count of 0 [RedeclParamCountPrevious]
   // CHECK:STDERR:   virtual fn F[self: Self]();
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn F[self: Self](v: i32);
+  override fn F[self: Self](v: i32);
 }
 
 // --- fail_todo_impl_conversion.carbon
@@ -223,13 +223,13 @@ base class Base {
 class Derived {
   extend base: Base;
   // CHECK:STDERR: fail_todo_impl_conversion.carbon:[[@LINE+7]]:3: error: function redeclaration differs because return type is `T2` [FunctionRedeclReturnTypeDiffers]
-  // CHECK:STDERR:   impl fn F[self: Self]() -> T2;
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:   override fn F[self: Self]() -> T2;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_todo_impl_conversion.carbon:[[@LINE-8]]:3: note: previously declared with return type `T1` [FunctionRedeclReturnTypePrevious]
   // CHECK:STDERR:   virtual fn F[self: Self]() -> T1;
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn F[self: Self]() -> T2;
+  override fn F[self: Self]() -> T2;
 }
 
 // --- fail_generic_virtual_decl.carbon
@@ -261,7 +261,7 @@ base class Base(T:! type) {
 
 class Derived {
   extend base: Base(T1);
-  impl fn F[self: Self](t: T1) { }
+  override fn F[self: Self](t: T1) { }
 }
 
 // --- fail_virtual_without_self.carbon
@@ -284,10 +284,10 @@ abstract class T1 {
 class T2 {
   extend base: T1;
   // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf]
-  // CHECK:STDERR:   impl fn F();
-  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR:   override fn F();
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn F();
+  override fn F();
 }
 
 // --- fail_addr_self_mismatch.carbon
@@ -300,14 +300,14 @@ base class T1 {
 
 class T2 {
   extend base: T1;
-  // CHECK:STDERR: fail_addr_self_mismatch.carbon:[[@LINE+7]]:14: error: redeclaration differs at implicit parameter 1 [RedeclParamDiffers]
-  // CHECK:STDERR:   impl fn F1[addr self: Self*]();
-  // CHECK:STDERR:              ^~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_addr_self_mismatch.carbon:[[@LINE+7]]:18: error: redeclaration differs at implicit parameter 1 [RedeclParamDiffers]
+  // CHECK:STDERR:   override fn F1[addr self: Self*]();
+  // CHECK:STDERR:                  ^~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_addr_self_mismatch.carbon:[[@LINE-8]]:17: note: previous declaration's corresponding implicit parameter here [RedeclParamPrevious]
   // CHECK:STDERR:   virtual fn F1[self: Self*]();
   // CHECK:STDERR:                 ^~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn F1[addr self: Self*]();
+  override fn F1[addr self: Self*]();
 }
 
 // --- fail_generic_virtual.carbon
@@ -369,7 +369,7 @@ base class NonGenericBase {
 
 base class GenericDerived(T:! type) {
   extend base: NonGenericBase;
-  impl fn F2[self: Self]() { }
+  override fn F2[self: Self]() { }
   virtual fn F3[self: Self]() { }
 }
 
@@ -386,7 +386,7 @@ class T1;
 
 base class NonGenericDerived {
   extend base: GenericBase(T1);
-  impl fn F2[self: Self]() { }
+  override fn F2[self: Self]() { }
   virtual fn F3[self: Self]() { }
 }
 
@@ -400,7 +400,7 @@ base class Base(T:! type) {
 class T1;
 class D1 {
   extend base: Base(T1);
-  impl fn F[self: Self](t: Base(T1)*) { }
+  override fn F[self: Self](t: Base(T1)*) { }
 }
 
 // --- fail_impl_generic_specifically_mismatch.carbon
@@ -416,14 +416,14 @@ base class Base(T:! type) {
 
 class D1 {
   extend base: Base(T1);
-  // CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE+7]]:25: error: type `<pattern for T2*>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T1*>` [RedeclParamDiffersType]
-  // CHECK:STDERR:   impl fn F[self: Self](t: T2*) { }
-  // CHECK:STDERR:                         ^~~~~~
+  // CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE+7]]:29: error: type `<pattern for T2*>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T1*>` [RedeclParamDiffersType]
+  // CHECK:STDERR:   override fn F[self: Self](t: T2*) { }
+  // CHECK:STDERR:                             ^~~~~~
   // CHECK:STDERR: fail_impl_generic_specifically_mismatch.carbon:[[@LINE-8]]:28: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
   // CHECK:STDERR:   virtual fn F[self: Self](t: T1*) { }
   // CHECK:STDERR:                            ^~~~~~
   // CHECK:STDERR:
-  impl fn F[self: Self](t: T2*) { }
+  override fn F[self: Self](t: T2*) { }
 }
 
 // --- fail_impl_generic_generic_mismatch.carbon
@@ -435,14 +435,14 @@ abstract class Base(T:! type) {
 }
 class Derived(T:! type) {
   extend base: Base(T);
-  // CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE+7]]:25: error: type `<pattern for T>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T*>` [RedeclParamDiffersType]
-  // CHECK:STDERR:   impl fn F[self: Self](t: T) { }
-  // CHECK:STDERR:                         ^~~~
+  // CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE+7]]:29: error: type `<pattern for T>` of parameter 1 in redeclaration differs from previous parameter type `<pattern for T*>` [RedeclParamDiffersType]
+  // CHECK:STDERR:   override fn F[self: Self](t: T) { }
+  // CHECK:STDERR:                             ^~~~
   // CHECK:STDERR: fail_impl_generic_generic_mismatch.carbon:[[@LINE-7]]:28: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
   // CHECK:STDERR:   virtual fn F[self: Self](t: T*) { }
   // CHECK:STDERR:                            ^~~~~
   // CHECK:STDERR:
-  impl fn F[self: Self](t: T) { }
+  override fn F[self: Self](t: T) { }
 }
 
 // --- impl_generic_generic.carbon
@@ -454,7 +454,7 @@ abstract class Base(T:! type) {
 }
 class Derived(T:! type) {
   extend base: Base(T*);
-  impl fn F[self: Self](t: T*) { }
+  override fn F[self: Self](t: T*) { }
 }
 
 // --- abstract_generic_undefined.carbon
@@ -469,7 +469,7 @@ class T1;
 
 class Derived {
   extend base: Base(T1);
-  impl fn F[self: Self]() { }
+  override fn F[self: Self]() { }
 }
 
 // --- generic_lib.carbon
@@ -802,7 +802,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   @Derived.%Derived.H.decl
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.H(%self.param: %Derived);
+// CHECK:STDOUT: override fn @Derived.H(%self.param: %Derived);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Derived.as.Destroy.impl.Op(%self.param: %ptr.404) = "no_op";
 // CHECK:STDOUT:
@@ -1236,7 +1236,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @A1.as.Destroy.impl.Op(%self.param: %ptr.678) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @A2.F(%self.param: %A2);
+// CHECK:STDOUT: override fn @A2.F(%self.param: %A2);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @A2.as.Destroy.impl.Op(%self.param: %ptr.590) = "no_op";
 // CHECK:STDOUT:
@@ -1468,11 +1468,11 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @B1.as.Destroy.impl.Op(%self.param: %ptr.890) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @B2.F(%self.param: %B2);
+// CHECK:STDOUT: override fn @B2.F(%self.param: %B2);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @B2.as.Destroy.impl.Op(%self.param: %ptr.afe) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @C.F(%self.param: %C);
+// CHECK:STDOUT: override fn @C.F(%self.param: %C);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @C.as.Destroy.impl.Op(%self.param: %ptr.019) = "no_op";
 // CHECK:STDOUT:
@@ -1617,7 +1617,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   .F = %C.F.decl
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @C.F(%self.param: %C);
+// CHECK:STDOUT: override fn @C.F(%self.param: %C);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @C.as.Destroy.impl.Op(%self.param: %ptr.019) = "no_op";
 // CHECK:STDOUT:
@@ -2013,7 +2013,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Base.as.Destroy.impl.Op(%self.param: %ptr.11f) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived);
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Derived.as.Destroy.impl.Op(%self.param: %ptr.404) = "no_op";
 // CHECK:STDOUT:
@@ -2218,7 +2218,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @AbstractIntermediate.as.Destroy.impl.Op(%self.param: %ptr.fd8) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived);
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Derived.as.Destroy.impl.Op(%self.param: %ptr.404) = "no_op";
 // CHECK:STDOUT:
@@ -2423,7 +2423,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @VirtualIntermediate.as.Destroy.impl.Op(%self.param: %ptr.7dc) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived);
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Derived.as.Destroy.impl.Op(%self.param: %ptr.404) = "no_op";
 // CHECK:STDOUT:
@@ -2590,7 +2590,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Base.as.Destroy.impl.Op(%self.param: %ptr.11f) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived, %v.param: %i32);
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived, %v.param: %i32);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Derived.as.Destroy.impl.Op(%self.param: %ptr.404) = "no_op";
 // CHECK:STDOUT:
@@ -2882,7 +2882,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Base.as.Destroy.impl.Op(%self.param: %ptr.11f) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived) -> %return.param: %T2;
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived) -> %return.param: %T2;
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Derived.as.Destroy.impl.Op(%self.param: %ptr.404) = "no_op";
 // CHECK:STDOUT:
@@ -3320,7 +3320,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @Base.as.Destroy.impl.Op.%ptr (%ptr.b7c)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived, %t.param: %T1) {
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived, %t.param: %T1) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -3622,10 +3622,10 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   %T2.F1.decl: %T2.F1.type = fn_decl @T2.F1 [concrete = constants.%T2.F1] {
 // CHECK:STDOUT:     %self.patt: %pattern_type.fb8 = binding_pattern self [concrete]
 // CHECK:STDOUT:     %self.param_patt: %pattern_type.fb8 = value_param_pattern %self.patt, call_param0 [concrete]
-// CHECK:STDOUT:     %.loc17_14: %pattern_type.f6d = addr_pattern %self.param_patt [concrete]
+// CHECK:STDOUT:     %.loc17_18: %pattern_type.f6d = addr_pattern %self.param_patt [concrete]
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %self.param: %ptr.63e = value_param call_param0
-// CHECK:STDOUT:     %.loc17_29: type = splice_block %ptr [concrete = constants.%ptr.63e] {
+// CHECK:STDOUT:     %.loc17_33: type = splice_block %ptr [concrete = constants.%ptr.63e] {
 // CHECK:STDOUT:       %Self.ref: type = name_ref Self, constants.%T2 [concrete = constants.%T2]
 // CHECK:STDOUT:       %ptr: type = ptr_type %Self.ref [concrete = constants.%ptr.63e]
 // CHECK:STDOUT:     }
@@ -3660,7 +3660,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @T1.as.Destroy.impl.Op(%self.param: %ptr.87b) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @T2.F1(%self.param: %ptr.63e);
+// CHECK:STDOUT: override fn @T2.F1(%self.param: %ptr.63e);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @T2.as.Destroy.impl.Op(%self.param: %ptr.63e) = "no_op";
 // CHECK:STDOUT:
@@ -4505,9 +4505,9 @@ class T2(G2:! type) {
 // CHECK:STDOUT:       %self.param_patt: @GenericDerived.F2.%pattern_type (%pattern_type.061) = value_param_pattern %self.patt, call_param0 [concrete]
 // CHECK:STDOUT:     } {
 // CHECK:STDOUT:       %self.param: @GenericDerived.F2.%GenericDerived (%GenericDerived) = value_param call_param0
-// CHECK:STDOUT:       %.loc11_20.1: type = splice_block %Self.ref [symbolic = %GenericDerived (constants.%GenericDerived)] {
-// CHECK:STDOUT:         %.loc11_20.2: type = specific_constant constants.%GenericDerived, @GenericDerived(constants.%T) [symbolic = %GenericDerived (constants.%GenericDerived)]
-// CHECK:STDOUT:         %Self.ref: type = name_ref Self, %.loc11_20.2 [symbolic = %GenericDerived (constants.%GenericDerived)]
+// CHECK:STDOUT:       %.loc11_24.1: type = splice_block %Self.ref [symbolic = %GenericDerived (constants.%GenericDerived)] {
+// CHECK:STDOUT:         %.loc11_24.2: type = specific_constant constants.%GenericDerived, @GenericDerived(constants.%T) [symbolic = %GenericDerived (constants.%GenericDerived)]
+// CHECK:STDOUT:         %Self.ref: type = name_ref Self, %.loc11_24.2 [symbolic = %GenericDerived (constants.%GenericDerived)]
 // CHECK:STDOUT:       }
 // CHECK:STDOUT:       %self: @GenericDerived.F2.%GenericDerived (%GenericDerived) = bind_name self, %self.param
 // CHECK:STDOUT:     }
@@ -4566,7 +4566,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @NonGenericBase.as.Destroy.impl.Op(%self.param: %ptr.806) = "no_op";
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl fn @GenericDerived.F2(@GenericDerived.%T.loc9_27.2: type) {
+// CHECK:STDOUT: generic override fn @GenericDerived.F2(@GenericDerived.%T.loc9_27.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %GenericDerived: type = class_type @GenericDerived, @GenericDerived(%T) [symbolic = %GenericDerived (constants.%GenericDerived)]
 // CHECK:STDOUT:   %pattern_type: type = pattern_type %GenericDerived [symbolic = %pattern_type (constants.%pattern_type.061)]
@@ -4574,7 +4574,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %GenericDerived [symbolic = %require_complete (constants.%require_complete)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl fn(%self.param: @GenericDerived.F2.%GenericDerived (%GenericDerived)) {
+// CHECK:STDOUT:   override fn(%self.param: @GenericDerived.F2.%GenericDerived (%GenericDerived)) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
@@ -4921,7 +4921,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @GenericBase.as.Destroy.impl.Op.%ptr (%ptr.b68)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @NonGenericDerived.F2(%self.param: %NonGenericDerived) {
+// CHECK:STDOUT: override fn @NonGenericDerived.F2(%self.param: %NonGenericDerived) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -5257,7 +5257,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @Base.as.Destroy.impl.Op.%ptr (%ptr.b7c)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @D1.F(%self.param: %D1, %t.param: %ptr.184) {
+// CHECK:STDOUT: override fn @D1.F(%self.param: %D1, %t.param: %ptr.184) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -5568,7 +5568,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @Base.as.Destroy.impl.Op.%ptr (%ptr.b7c)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @D1.F(%self.param: %D1, %t.param: %ptr.63e) {
+// CHECK:STDOUT: override fn @D1.F(%self.param: %D1, %t.param: %ptr.63e) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
@@ -5827,15 +5827,15 @@ class T2(G2:! type) {
 // CHECK:STDOUT:     %Base.loc8_22.1: type = class_type @Base, @Base(constants.%T) [symbolic = %Base.loc8_22.2 (constants.%Base)]
 // CHECK:STDOUT:     %.loc8: @Derived.%Derived.elem (%Derived.elem) = base_decl %Base.loc8_22.1, element0 [concrete]
 // CHECK:STDOUT:     %Derived.F.decl: @Derived.%Derived.F.type (%Derived.F.type) = fn_decl @Derived.F [symbolic = @Derived.%Derived.F (constants.%Derived.F)] {
-// CHECK:STDOUT:       %self.patt: @Derived.F.%pattern_type.loc16_13 (%pattern_type.423) = binding_pattern self [concrete]
-// CHECK:STDOUT:       %self.param_patt: @Derived.F.%pattern_type.loc16_13 (%pattern_type.423) = value_param_pattern %self.patt, call_param0 [concrete]
-// CHECK:STDOUT:       %t.patt: @Derived.F.%pattern_type.loc16_25 (%pattern_type.7dc) = binding_pattern t [concrete]
-// CHECK:STDOUT:       %t.param_patt: @Derived.F.%pattern_type.loc16_25 (%pattern_type.7dc) = value_param_pattern %t.patt, call_param1 [concrete]
+// CHECK:STDOUT:       %self.patt: @Derived.F.%pattern_type.loc16_17 (%pattern_type.423) = binding_pattern self [concrete]
+// CHECK:STDOUT:       %self.param_patt: @Derived.F.%pattern_type.loc16_17 (%pattern_type.423) = value_param_pattern %self.patt, call_param0 [concrete]
+// CHECK:STDOUT:       %t.patt: @Derived.F.%pattern_type.loc16_29 (%pattern_type.7dc) = binding_pattern t [concrete]
+// CHECK:STDOUT:       %t.param_patt: @Derived.F.%pattern_type.loc16_29 (%pattern_type.7dc) = value_param_pattern %t.patt, call_param1 [concrete]
 // CHECK:STDOUT:     } {
 // CHECK:STDOUT:       %self.param: @Derived.F.%Derived (%Derived) = value_param call_param0
-// CHECK:STDOUT:       %.loc16_19.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] {
-// CHECK:STDOUT:         %.loc16_19.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)]
-// CHECK:STDOUT:         %Self.ref: type = name_ref Self, %.loc16_19.2 [symbolic = %Derived (constants.%Derived)]
+// CHECK:STDOUT:       %.loc16_23.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] {
+// CHECK:STDOUT:         %.loc16_23.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)]
+// CHECK:STDOUT:         %Self.ref: type = name_ref Self, %.loc16_23.2 [symbolic = %Derived (constants.%Derived)]
 // CHECK:STDOUT:       }
 // CHECK:STDOUT:       %self: @Derived.F.%Derived (%Derived) = bind_name self, %self.param
 // CHECK:STDOUT:       %t.param: @Derived.F.%T (%T) = value_param call_param1
@@ -5898,19 +5898,19 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @Base.as.Destroy.impl.Op.%ptr (%ptr.b7c)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl fn @Derived.F(@Derived.%T.loc7_15.2: type) {
+// CHECK:STDOUT: generic override fn @Derived.F(@Derived.%T.loc7_15.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %Derived: type = class_type @Derived, @Derived(%T) [symbolic = %Derived (constants.%Derived)]
-// CHECK:STDOUT:   %pattern_type.loc16_13: type = pattern_type %Derived [symbolic = %pattern_type.loc16_13 (constants.%pattern_type.423)]
+// CHECK:STDOUT:   %pattern_type.loc16_17: type = pattern_type %Derived [symbolic = %pattern_type.loc16_17 (constants.%pattern_type.423)]
 // CHECK:STDOUT:   %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base)]
-// CHECK:STDOUT:   %require_complete.loc16_28: <witness> = require_complete_type %Base [symbolic = %require_complete.loc16_28 (constants.%require_complete.97d)]
-// CHECK:STDOUT:   %pattern_type.loc16_25: type = pattern_type %T [symbolic = %pattern_type.loc16_25 (constants.%pattern_type.7dc)]
+// CHECK:STDOUT:   %require_complete.loc16_32: <witness> = require_complete_type %Base [symbolic = %require_complete.loc16_32 (constants.%require_complete.97d)]
+// CHECK:STDOUT:   %pattern_type.loc16_29: type = pattern_type %T [symbolic = %pattern_type.loc16_29 (constants.%pattern_type.7dc)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete.loc16_17: <witness> = require_complete_type %Derived [symbolic = %require_complete.loc16_17 (constants.%require_complete.5f4)]
-// CHECK:STDOUT:   %require_complete.loc16_26: <witness> = require_complete_type %T [symbolic = %require_complete.loc16_26 (constants.%require_complete.4ae)]
+// CHECK:STDOUT:   %require_complete.loc16_21: <witness> = require_complete_type %Derived [symbolic = %require_complete.loc16_21 (constants.%require_complete.5f4)]
+// CHECK:STDOUT:   %require_complete.loc16_30: <witness> = require_complete_type %T [symbolic = %require_complete.loc16_30 (constants.%require_complete.4ae)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%T (%T)) {
+// CHECK:STDOUT:   override fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%T (%T)) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
@@ -5979,14 +5979,14 @@ class T2(G2:! type) {
 // CHECK:STDOUT: specific @Derived.F(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %Derived => constants.%Derived
-// CHECK:STDOUT:   %pattern_type.loc16_13 => constants.%pattern_type.423
+// CHECK:STDOUT:   %pattern_type.loc16_17 => constants.%pattern_type.423
 // CHECK:STDOUT:   %Base => constants.%Base
-// CHECK:STDOUT:   %require_complete.loc16_28 => constants.%require_complete.97d
-// CHECK:STDOUT:   %pattern_type.loc16_25 => constants.%pattern_type.7dc
+// CHECK:STDOUT:   %require_complete.loc16_32 => constants.%require_complete.97d
+// CHECK:STDOUT:   %pattern_type.loc16_29 => constants.%pattern_type.7dc
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete.loc16_17 => constants.%require_complete.5f4
-// CHECK:STDOUT:   %require_complete.loc16_26 => constants.%require_complete.4ae
+// CHECK:STDOUT:   %require_complete.loc16_21 => constants.%require_complete.5f4
+// CHECK:STDOUT:   %require_complete.loc16_30 => constants.%require_complete.4ae
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Derived.as.Destroy.impl(constants.%T) {
@@ -6210,23 +6210,23 @@ class T2(G2:! type) {
 // CHECK:STDOUT:     %Base.loc8_23.1: type = class_type @Base, @Base(constants.%ptr.79f) [symbolic = %Base.loc8_23.2 (constants.%Base.16b)]
 // CHECK:STDOUT:     %.loc8: @Derived.%Derived.elem (%Derived.elem) = base_decl %Base.loc8_23.1, element0 [concrete]
 // CHECK:STDOUT:     %Derived.F.decl: @Derived.%Derived.F.type (%Derived.F.type) = fn_decl @Derived.F [symbolic = @Derived.%Derived.F (constants.%Derived.F)] {
-// CHECK:STDOUT:       %self.patt: @Derived.F.%pattern_type.loc9_13 (%pattern_type.423) = binding_pattern self [concrete]
-// CHECK:STDOUT:       %self.param_patt: @Derived.F.%pattern_type.loc9_13 (%pattern_type.423) = value_param_pattern %self.patt, call_param0 [concrete]
-// CHECK:STDOUT:       %t.patt: @Derived.F.%pattern_type.loc9_25 (%pattern_type.afe) = binding_pattern t [concrete]
-// CHECK:STDOUT:       %t.param_patt: @Derived.F.%pattern_type.loc9_25 (%pattern_type.afe) = value_param_pattern %t.patt, call_param1 [concrete]
+// CHECK:STDOUT:       %self.patt: @Derived.F.%pattern_type.loc9_17 (%pattern_type.423) = binding_pattern self [concrete]
+// CHECK:STDOUT:       %self.param_patt: @Derived.F.%pattern_type.loc9_17 (%pattern_type.423) = value_param_pattern %self.patt, call_param0 [concrete]
+// CHECK:STDOUT:       %t.patt: @Derived.F.%pattern_type.loc9_29 (%pattern_type.afe) = binding_pattern t [concrete]
+// CHECK:STDOUT:       %t.param_patt: @Derived.F.%pattern_type.loc9_29 (%pattern_type.afe) = value_param_pattern %t.patt, call_param1 [concrete]
 // CHECK:STDOUT:     } {
 // CHECK:STDOUT:       %self.param: @Derived.F.%Derived (%Derived) = value_param call_param0
-// CHECK:STDOUT:       %.loc9_19.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] {
-// CHECK:STDOUT:         %.loc9_19.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)]
-// CHECK:STDOUT:         %Self.ref: type = name_ref Self, %.loc9_19.2 [symbolic = %Derived (constants.%Derived)]
+// CHECK:STDOUT:       %.loc9_23.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] {
+// CHECK:STDOUT:         %.loc9_23.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)]
+// CHECK:STDOUT:         %Self.ref: type = name_ref Self, %.loc9_23.2 [symbolic = %Derived (constants.%Derived)]
 // CHECK:STDOUT:       }
 // CHECK:STDOUT:       %self: @Derived.F.%Derived (%Derived) = bind_name self, %self.param
-// CHECK:STDOUT:       %t.param: @Derived.F.%ptr.loc9_28 (%ptr.79f) = value_param call_param1
-// CHECK:STDOUT:       %.loc9_29: type = splice_block %ptr.loc9_29 [symbolic = %ptr.loc9_28 (constants.%ptr.79f)] {
+// CHECK:STDOUT:       %t.param: @Derived.F.%ptr.loc9_32 (%ptr.79f) = value_param call_param1
+// CHECK:STDOUT:       %.loc9_33: type = splice_block %ptr.loc9_33 [symbolic = %ptr.loc9_32 (constants.%ptr.79f)] {
 // CHECK:STDOUT:         %T.ref: type = name_ref T, @Derived.%T.loc7_15.2 [symbolic = %T (constants.%T)]
-// CHECK:STDOUT:         %ptr.loc9_29: type = ptr_type %T.ref [symbolic = %ptr.loc9_28 (constants.%ptr.79f)]
+// CHECK:STDOUT:         %ptr.loc9_33: type = ptr_type %T.ref [symbolic = %ptr.loc9_32 (constants.%ptr.79f)]
 // CHECK:STDOUT:       }
-// CHECK:STDOUT:       %t: @Derived.F.%ptr.loc9_28 (%ptr.79f) = bind_name t, %t.param
+// CHECK:STDOUT:       %t: @Derived.F.%ptr.loc9_32 (%ptr.79f) = bind_name t, %t.param
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%Derived [symbolic = @Derived.as.Destroy.impl.%Derived (constants.%Derived)]
 // CHECK:STDOUT:     impl_decl @Derived.as.Destroy.impl [concrete] {} {}
@@ -6283,20 +6283,20 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @Base.as.Destroy.impl.Op.%ptr (%ptr.b7c)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl fn @Derived.F(@Derived.%T.loc7_15.2: type) {
+// CHECK:STDOUT: generic override fn @Derived.F(@Derived.%T.loc7_15.2: type) {
 // CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %Derived: type = class_type @Derived, @Derived(%T) [symbolic = %Derived (constants.%Derived)]
-// CHECK:STDOUT:   %pattern_type.loc9_13: type = pattern_type %Derived [symbolic = %pattern_type.loc9_13 (constants.%pattern_type.423)]
-// CHECK:STDOUT:   %ptr.loc9_28: type = ptr_type %T [symbolic = %ptr.loc9_28 (constants.%ptr.79f)]
-// CHECK:STDOUT:   %Base: type = class_type @Base, @Base(%ptr.loc9_28) [symbolic = %Base (constants.%Base.16b)]
-// CHECK:STDOUT:   %require_complete.loc9_28: <witness> = require_complete_type %Base [symbolic = %require_complete.loc9_28 (constants.%require_complete.fce)]
-// CHECK:STDOUT:   %pattern_type.loc9_25: type = pattern_type %ptr.loc9_28 [symbolic = %pattern_type.loc9_25 (constants.%pattern_type.afe)]
+// CHECK:STDOUT:   %pattern_type.loc9_17: type = pattern_type %Derived [symbolic = %pattern_type.loc9_17 (constants.%pattern_type.423)]
+// CHECK:STDOUT:   %ptr.loc9_32: type = ptr_type %T [symbolic = %ptr.loc9_32 (constants.%ptr.79f)]
+// CHECK:STDOUT:   %Base: type = class_type @Base, @Base(%ptr.loc9_32) [symbolic = %Base (constants.%Base.16b)]
+// CHECK:STDOUT:   %require_complete.loc9_32: <witness> = require_complete_type %Base [symbolic = %require_complete.loc9_32 (constants.%require_complete.fce)]
+// CHECK:STDOUT:   %pattern_type.loc9_29: type = pattern_type %ptr.loc9_32 [symbolic = %pattern_type.loc9_29 (constants.%pattern_type.afe)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete.loc9_17: <witness> = require_complete_type %Derived [symbolic = %require_complete.loc9_17 (constants.%require_complete.5f4)]
-// CHECK:STDOUT:   %require_complete.loc9_26: <witness> = require_complete_type %ptr.loc9_28 [symbolic = %require_complete.loc9_26 (constants.%require_complete.6e5)]
+// CHECK:STDOUT:   %require_complete.loc9_21: <witness> = require_complete_type %Derived [symbolic = %require_complete.loc9_21 (constants.%require_complete.5f4)]
+// CHECK:STDOUT:   %require_complete.loc9_30: <witness> = require_complete_type %ptr.loc9_32 [symbolic = %require_complete.loc9_30 (constants.%require_complete.6e5)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%ptr.loc9_28 (%ptr.79f)) {
+// CHECK:STDOUT:   override fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%ptr.loc9_32 (%ptr.79f)) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
@@ -6385,15 +6385,15 @@ class T2(G2:! type) {
 // CHECK:STDOUT: specific @Derived.F(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %Derived => constants.%Derived
-// CHECK:STDOUT:   %pattern_type.loc9_13 => constants.%pattern_type.423
-// CHECK:STDOUT:   %ptr.loc9_28 => constants.%ptr.79f
+// CHECK:STDOUT:   %pattern_type.loc9_17 => constants.%pattern_type.423
+// CHECK:STDOUT:   %ptr.loc9_32 => constants.%ptr.79f
 // CHECK:STDOUT:   %Base => constants.%Base.16b
-// CHECK:STDOUT:   %require_complete.loc9_28 => constants.%require_complete.fce
-// CHECK:STDOUT:   %pattern_type.loc9_25 => constants.%pattern_type.afe
+// CHECK:STDOUT:   %require_complete.loc9_32 => constants.%require_complete.fce
+// CHECK:STDOUT:   %pattern_type.loc9_29 => constants.%pattern_type.afe
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete.loc9_17 => constants.%require_complete.5f4
-// CHECK:STDOUT:   %require_complete.loc9_26 => constants.%require_complete.6e5
+// CHECK:STDOUT:   %require_complete.loc9_21 => constants.%require_complete.5f4
+// CHECK:STDOUT:   %require_complete.loc9_30 => constants.%require_complete.6e5
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Derived.as.Destroy.impl(constants.%T) {
@@ -6623,7 +6623,7 @@ class T2(G2:! type) {
 // CHECK:STDOUT:   fn(%self.param: @Base.as.Destroy.impl.Op.%ptr (%ptr.b7c)) = "no_op";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: impl fn @Derived.F(%self.param: %Derived) {
+// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 4 - 4
toolchain/check/testdata/function/definition/fail_local_decl.carbon

@@ -22,11 +22,11 @@ fn F() {
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:
   default fn F();
-  // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `impl` not allowed; requires class scope [ModifierRequiresClass]
-  // CHECK:STDERR:   impl fn G();
-  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `override` not allowed; requires class scope [ModifierRequiresClass]
+  // CHECK:STDERR:   override fn G();
+  // CHECK:STDERR:   ^~~~~~~~
   // CHECK:STDERR:
-  impl fn G();
+  override fn G();
   // CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:3: error: `virtual` not allowed; requires class scope [ModifierRequiresClass]
   // CHECK:STDERR:   virtual fn H();
   // CHECK:STDERR:   ^~~~~~~

+ 4 - 4
toolchain/check/testdata/interop/cpp/class/abstract.carbon

@@ -46,11 +46,11 @@ import Cpp library "abstract.h";
 
 class C {
   extend base: Cpp.A;
-  // CHECK:STDERR: fail_todo_impl_abstract_member.carbon:[[@LINE+4]]:3: error: impl without compatible virtual in base class [ImplWithoutVirtualInBase]
-  // CHECK:STDERR:   impl fn f[addr self: Self*]() {}
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_todo_impl_abstract_member.carbon:[[@LINE+4]]:3: error: override without compatible virtual in base class [OverrideWithoutVirtualInBase]
+  // CHECK:STDERR:   override fn f[addr self: Self*]() {}
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  impl fn f[addr self: Self*]() {}
+  override fn f[addr self: Self*]() {}
 }
 
 // --- abstract_final.h

+ 2 - 2
toolchain/diagnostics/diagnostic_kind.def

@@ -289,8 +289,8 @@ CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclOutsideClass)
 CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclPrevious)
 CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition)
 CARBON_DIAGNOSTIC_KIND(GenericVirtual)
-CARBON_DIAGNOSTIC_KIND(ImplWithoutBase)
-CARBON_DIAGNOSTIC_KIND(ImplWithoutVirtualInBase)
+CARBON_DIAGNOSTIC_KIND(OverrideWithoutBase)
+CARBON_DIAGNOSTIC_KIND(OverrideWithoutVirtualInBase)
 CARBON_DIAGNOSTIC_KIND(VirtualWithoutSelf)
 
 // Deduction.

+ 1 - 0
toolchain/lex/token_kind.def

@@ -199,6 +199,7 @@ CARBON_KEYWORD_TOKEN(Not,                 "not")
 CARBON_KEYWORD_TOKEN(Observe,             "observe")
 CARBON_TOKEN_WITH_VIRTUAL_NODE(
   CARBON_KEYWORD_TOKEN(Or,                "or"))
+CARBON_KEYWORD_TOKEN(Override,            "override")
 CARBON_KEYWORD_TOKEN(Partial,             "partial")
 CARBON_KEYWORD_TOKEN(Private,             "private")
 CARBON_KEYWORD_TOKEN(Protected,           "protected")

+ 2 - 2
toolchain/lower/testdata/class/virtual.carbon

@@ -24,7 +24,7 @@ base class Intermediate {
 
 class Derived {
   extend base: Intermediate;
-  impl fn Fn[self: Self]() { }
+  override fn Fn[self: Self]() { }
 }
 
 // --- create.carbon
@@ -100,7 +100,7 @@ base class Base {
 
 class Derived {
   extend base: Base;
-  impl fn F[self: Self]();
+  override fn F[self: Self]();
 }
 
 fn Use() {

+ 1 - 0
toolchain/parse/node_kind.def

@@ -344,6 +344,7 @@ CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Export)
 CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Extend)
 CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Final)
 CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Impl)
+CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Override)
 CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Private)
 CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Protected)
 CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Virtual)

+ 2 - 2
toolchain/parse/state.def

@@ -285,8 +285,8 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterParams)
 // ^~~~~~
 // final
 // ^~~~~
-// impl fn
-// ^~~~
+// override fn
+// ^~~~~~~~
 // private
 // ^~~~~~~
 // protected

+ 8 - 1
toolchain/parse/testdata/function/decl_statement.carbon

@@ -37,6 +37,7 @@ fn F() {
   namespace N;
   default fn F();
   impl fn G();
+  override fn H();
   virtual fn H();
   private var v: i32;
   protected var v: i32;
@@ -157,6 +158,12 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
+// CHECK:STDOUT:         {kind: 'OverrideModifier', text: 'override'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'H'},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'VirtualModifier', text: 'virtual'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'H'},
 // CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
@@ -176,6 +183,6 @@ fn F() {
 // CHECK:STDOUT:           {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'VariablePattern', text: 'var', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'VariableDecl', text: ';', subtree_size: 7},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 58},
+// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 64},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 8 - 8
toolchain/parse/testdata/function/declaration.carbon

@@ -58,10 +58,10 @@ fn destroy[self: Self]().Foo() {}
 
 // --- impl_fn.carbon
 
-impl fn F();
-abstract impl fn G();
-impl abstract fn H();
-private impl default fn I();
+override fn F();
+abstract override fn G();
+override abstract fn H();
+private override default fn I();
 
 // --- fail_identifier_instead_of_sig.carbon
 
@@ -346,20 +346,20 @@ fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'OverrideModifier', text: 'override'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
 // CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'AbstractModifier', text: 'abstract'},
-// CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'OverrideModifier', text: 'override'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'G'},
 // CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'OverrideModifier', text: 'override'},
 // CHECK:STDOUT:       {kind: 'AbstractModifier', text: 'abstract'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'H'},
 // CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
@@ -367,7 +367,7 @@ fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'PrivateModifier', text: 'private'},
-// CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
+// CHECK:STDOUT:       {kind: 'OverrideModifier', text: 'override'},
 // CHECK:STDOUT:       {kind: 'DefaultModifier', text: 'default'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'I'},
 // CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},

+ 2 - 2
toolchain/sem_ir/formatter.cpp

@@ -496,8 +496,8 @@ auto Formatter::FormatFunction(FunctionId id, const Function& fn) -> void {
     case FunctionFields::VirtualModifier::Abstract:
       function_start += "abstract ";
       break;
-    case FunctionFields::VirtualModifier::Impl:
-      function_start += "impl ";
+    case FunctionFields::VirtualModifier::Override:
+      function_start += "override ";
       break;
     case FunctionFields::VirtualModifier::None:
       break;

+ 1 - 1
toolchain/sem_ir/function.h

@@ -27,7 +27,7 @@ struct FunctionFields {
   };
 
   // Kinds of virtual modifiers that can apply to functions.
-  enum class VirtualModifier : uint8_t { None, Virtual, Abstract, Impl };
+  enum class VirtualModifier : uint8_t { None, Virtual, Abstract, Override };
 
   // The following members always have values, and do not change throughout the
   // lifetime of the function.