Move self to the explicit () parameter list (proposal #7016) (#7272)

Implements proposal #7016: `self` moves from the deduced implicit list
(`fn F[self: Self]()`) to the front of the explicit list. Its type may
be written explicitly (`fn F(self: Self)`) or omitted, in which case it
defaults to `Self` (`fn F(self)`, `fn F(ref self)`); `self` in the
implicit list is rejected.

Throughout checking, `self` is modeled as the first explicit parameter.
Because a method is just a function whose first parameter is `self`, it
can also be called as an ordinary function with the receiver passed
explicitly (`Type.M(obj, ...)`), not only as `obj.M(...)`. A new
`SemIR::CallArgParamPatterns` helper chooses the parameters matched
against the explicit arguments, excluding a leading `self` only when it
is supplied as a method-call receiver; arity checking, conversion, and
generic deduction use it. The resulting SemIR and lowering are
unchanged: `self` is still `call_param0`, and witnesses, thunks, and
vtables are unaffected.

An omitted `self` type is parsed as a `SelfBindingPattern` node with no
type expression; checking synthesizes the `Self` type so it behaves
exactly like `self: Self`. However, the exact spelling used must match
between a forward declaration and a definition, following #3763's rules
around declaration matching.

Generated functions, thunks, and C++ interop import/export build `self`
as the first explicit parameter, and the `self`-type override (e.g.
Derived->Base for a virtual override) applies to the explicit `self`.
Placement is validated by new diagnostics: `SelfInImplicitParamList`,
`SelfNotFirstParam`, and `SelfOutsideParamList`. The benchmark source
generator and the documentation adopt the `(self)` shorthand; the
prelude, the examples, and the test data are migrated in the following
commits.

Assisted-by: Claude Code with Claude Opus 4.7

---------

Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
This commit is contained in:
Chandler Carruth
2026-06-10 15:30:43 +00:00
committed by GitHub
co-authored by josh11b
parent fb05da761f
commit 7871237c15
296 changed files with 6101 additions and 4899 deletions
+5 -5
View File
@@ -15,23 +15,23 @@ export import library "prelude/parts/destroy";
export import library "prelude/parts/form";
interface UnsafeAs(Dest:! type) {
fn Convert[self: Self]() -> Dest;
fn Convert(self) -> Dest;
}
interface As(Dest:! type) {
// TODO: extend UnsafeAs(Dest);
fn Convert[self: Self]() -> Dest;
fn Convert(self) -> Dest;
}
interface ImplicitAs(Dest:! type) {
// TODO: extend As(Dest);
fn Convert[self: Self]() -> Dest;
fn Convert(self) -> Dest;
}
interface BitAndWith(Other:! type) {
fn Op[self: Self](other: Other) -> Self;
fn Op(self, other: Other) -> Self;
}
impl type as BitAndWith(type) {
fn Op[self: Self](other: Self) -> Self = "type.and";
fn Op(self, other: Self) -> Self = "type.and";
}
+1 -1
View File
@@ -18,5 +18,5 @@ class Char {
}
impl CharLiteral as ImplicitAs(Char) {
fn Convert[self: Self]() -> Char = "char.convert_checked";
fn Convert(self) -> Char = "char.convert_checked";
}
+11 -11
View File
@@ -9,7 +9,7 @@ package Core library "prelude/parts/copy";
// Copying an object, which is a conversion from a value representation to an
// initializing representation.
interface Copy {
fn Op[self: Self]() -> Self;
fn Op(self) -> Self;
}
private fn MakeBool() -> type = "bool.make_type";
@@ -22,45 +22,45 @@ private fn MakeIntLiteral() -> type = "int_literal.make_type";
private alias IntLiteral = MakeIntLiteral();
impl forall [T:! Copy] const T as Copy {
fn Op[self: Self]() -> Self { return (self as T).(Copy.Op)() as const T; }
fn Op(self) -> Self { return (self as T).(Copy.Op)() as const T; }
}
impl Bool as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl CharLiteral as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl FloatLiteral as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl IntLiteral as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl forall [T:! type] T* as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl type as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl () as Copy {
fn Op[self: Self]() -> Self = "no_op";
fn Op(self) -> Self = "no_op";
}
impl forall [T:! Copy, U:! Copy] (T, U) as Copy {
fn Op[self: Self]() -> Self {
fn Op(self) -> Self {
return (self.0.Op(), self.1.Op());
}
}
impl forall [T:! Copy, U:! Copy, V:! Copy] (T, U, V) as Copy {
fn Op[self: Self]() -> Self {
fn Op(self) -> Self {
return (self.0.Op(), self.1.Op(), self.2.Op());
}
}
@@ -8,12 +8,12 @@ package Core library "prelude/parts/destroy";
// TODO: Add `Destructor`, as in:
// interface Destructor {
// private fn Op[ref self: Self]();
// private fn Op(ref self);
// }
// Destroys objects. This will invoke `Destructor` impls recursively on members;
// it does not deallocate memory.
interface Destroy {
// TODO: This should be `final fn Op[ref self: Self]() = "type.destroy"`.
fn Op[ref self: Self]();
// TODO: This should be `final fn Op(ref self) = "type.destroy"`.
fn Op(ref self);
}
+3 -3
View File
@@ -23,14 +23,14 @@ class Float(N:! IntLiteral) {
}
impl forall [N:! IntLiteral] Float(N) as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
impl forall [To:! IntLiteral] FloatLiteral as ImplicitAs(Float(To)) {
fn Convert[self: Self]() -> Float(To) = "float.convert_checked";
fn Convert(self) -> Float(To) = "float.convert_checked";
}
// TODO: Remove this once ImplicitAs extends As.
impl forall [To:! IntLiteral] FloatLiteral as As(Float(To)) {
fn Convert[self: Self]() -> Float(To) = "float.convert_checked";
fn Convert(self) -> Float(To) = "float.convert_checked";
}
+6 -6
View File
@@ -23,31 +23,31 @@ class Int(N:! IntLiteral) {
// Copy.
impl forall [N:! IntLiteral] Int(N) as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
// Conversions.
impl forall [To:! IntLiteral] IntLiteral as ImplicitAs(Int(To)) {
fn Convert[self: Self]() -> Int(To) = "int.convert_checked";
fn Convert(self) -> Int(To) = "int.convert_checked";
}
final impl forall [From:! IntLiteral] Int(From) as ImplicitAs(IntLiteral) {
fn Convert[self: Self]() -> IntLiteral = "int.convert_checked";
fn Convert(self) -> IntLiteral = "int.convert_checked";
}
// TODO: Remove these once ImplicitAs extends As.
impl forall [To:! IntLiteral] IntLiteral as As(Int(To)) {
fn Convert[self: Self]() -> Int(To) = "int.convert_checked";
fn Convert(self) -> Int(To) = "int.convert_checked";
}
final impl forall [From:! IntLiteral] Int(From) as As(IntLiteral) {
fn Convert[self: Self]() -> IntLiteral = "int.convert_checked";
fn Convert(self) -> IntLiteral = "int.convert_checked";
}
// Negate.
final impl forall [N:! IntLiteral]
Int(N) as Negate where .Result = Self {
fn Op[self: Self]() -> Self = "int.snegate";
fn Op(self) -> Self = "int.snegate";
}
@@ -13,9 +13,9 @@ alias IntLiteral = MakeIntLiteral();
interface Negate {
let Result:! type;
fn Op[self: Self]() -> Result;
fn Op(self) -> Result;
}
impl IntLiteral as Negate where .Result = Self {
fn Op[self: Self]() -> Self = "int.snegate";
fn Op(self) -> Self = "int.snegate";
}
@@ -15,6 +15,6 @@ import library "prelude/parts/optional";
interface Iterate {
let ElementType:! Copy;
let CursorType:! type;
fn NewCursor[self: Self]() -> CursorType;
fn Next[self: Self](cursor: CursorType*) -> Optional(ElementType);
fn NewCursor(self) -> CursorType;
fn Next(self, cursor: CursorType*) -> Optional(ElementType);
}
@@ -27,8 +27,8 @@ class Optional(T:! Copy) {
return {.has_value = true, .value = value};
}
fn HasValue[self: Self]() -> bool { return self.has_value; }
fn Get[self: Self]() -> T { return self.value; }
fn HasValue(self) -> bool { return self.has_value; }
fn Get(self) -> T { return self.value; }
var has_value: bool;
var value: T;
+6 -6
View File
@@ -23,31 +23,31 @@ class UInt(N:! IntLiteral) {
// Copy.
impl forall [N:! IntLiteral] UInt(N) as Copy {
fn Op[self: Self]() -> Self = "primitive_copy";
fn Op(self) -> Self = "primitive_copy";
}
// Conversions.
impl forall [To:! IntLiteral] IntLiteral as ImplicitAs(UInt(To)) {
fn Convert[self: Self]() -> UInt(To) = "int.convert_checked";
fn Convert(self) -> UInt(To) = "int.convert_checked";
}
final impl forall [From:! IntLiteral] UInt(From) as ImplicitAs(IntLiteral) {
fn Convert[self: Self]() -> IntLiteral = "int.convert_checked";
fn Convert(self) -> IntLiteral = "int.convert_checked";
}
// TODO: Remove these once ImplicitAs extends As.
impl forall [To:! IntLiteral] IntLiteral as As(UInt(To)) {
fn Convert[self: Self]() -> UInt(To) = "int.convert_checked";
fn Convert(self) -> UInt(To) = "int.convert_checked";
}
final impl forall [From:! IntLiteral] UInt(From) as As(IntLiteral) {
fn Convert[self: Self]() -> IntLiteral = "int.convert_checked";
fn Convert(self) -> IntLiteral = "int.convert_checked";
}
// Negate.
final impl forall [N:! IntLiteral]
UInt(N) as Negate where .Result = Self {
fn Op[self: Self]() -> Self = "int.unegate";
fn Op(self) -> Self = "int.unegate";
}