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
+2 -2
View File
@@ -15,11 +15,11 @@ class A {
fn Init() -> A { return {.v = (1, 2)}; }
fn Access[self: Self]() -> i32 {
fn Access(self) -> i32 {
return self.v[0];
}
fn Use[ref self: Self]() -> i32 {
fn Use(ref self) -> i32 {
self.v[0] = 1;
return self.v[1];
}
+8 -8
View File
@@ -32,7 +32,7 @@ fn F() {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %.loc16_43.3.temp = alloca [6 x i32], align 4, !dbg !7
// CHECK:STDOUT: %var = alloca i32, align 4, !dbg !8
// CHECK:STDOUT: %.loc17_19.1.temp = alloca <{ i32, i1 }>, align 4, !dbg !8
// CHECK:STDOUT: %.loc17_19.3.temp = alloca <{ i32, i1 }>, align 4, !dbg !8
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc16_43.3.temp), !dbg !7
// CHECK:STDOUT: %.loc16_43.4.array.index = getelementptr inbounds [6 x i32], ptr %.loc16_43.3.temp, i32 0, i64 0, !dbg !7
// CHECK:STDOUT: %.loc16_43.7.array.index = getelementptr inbounds [6 x i32], ptr %.loc16_43.3.temp, i32 0, i64 1, !dbg !7
@@ -46,18 +46,18 @@ fn F() {
// CHECK:STDOUT: br label %for.next, !dbg !8
// CHECK:STDOUT:
// CHECK:STDOUT: for.next: ; preds = %for.body, %entry
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc17_19.1.temp), !dbg !8
// CHECK:STDOUT: call void @"_CNext.e163a239f9fa927c:Iterate.Core.620427d555024fe2"(ptr %.loc17_19.1.temp, ptr @array.loc16_43.24, ptr %var), !dbg !8
// CHECK:STDOUT: %Optional.HasValue.call = call i1 @_CHasValue.Optional.Core.18012f5191ea64dc(ptr %.loc17_19.1.temp), !dbg !8
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc17_19.3.temp), !dbg !8
// CHECK:STDOUT: call void @"_CNext.e163a239f9fa927c:Iterate.Core.620427d555024fe2"(ptr %.loc17_19.3.temp, ptr @array.loc16_43.24, ptr %var), !dbg !8
// CHECK:STDOUT: %Optional.HasValue.call = call i1 @_CHasValue.Optional.Core.18012f5191ea64dc(ptr %.loc17_19.3.temp), !dbg !8
// CHECK:STDOUT: br i1 %Optional.HasValue.call, label %for.body, label %for.done, !dbg !8
// CHECK:STDOUT:
// CHECK:STDOUT: for.body: ; preds = %for.next
// CHECK:STDOUT: %Optional.Get.call = call i32 @_CGet.Optional.Core.18012f5191ea64dc(ptr %.loc17_19.1.temp), !dbg !8
// CHECK:STDOUT: %Optional.Get.call = call i32 @_CGet.Optional.Core.18012f5191ea64dc(ptr %.loc17_19.3.temp), !dbg !8
// CHECK:STDOUT: call void @_CG.Main(i32 %Optional.Get.call), !dbg !9
// CHECK:STDOUT: br label %for.next, !dbg !10
// CHECK:STDOUT:
// CHECK:STDOUT: for.done: ; preds = %for.next
// CHECK:STDOUT: call void @"_COp.ab782c7eec490fc7:core.Destroy.Core"(ptr %.loc17_19.1.temp), !dbg !8
// CHECK:STDOUT: call void @"_COp.ab782c7eec490fc7:core.Destroy.Core"(ptr %.loc17_19.3.temp), !dbg !8
// CHECK:STDOUT: call void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %var), !dbg !8
// CHECK:STDOUT: call void @"_COp.3a5df7cec745952b:core.Destroy.Core"(ptr @array), !dbg !7
// CHECK:STDOUT: ret void, !dbg !11
@@ -276,7 +276,7 @@ fn F() {
// CHECK:STDOUT: !49 = !{!15, !22}
// CHECK:STDOUT: !50 = !{!51}
// CHECK:STDOUT: !51 = !DILocalVariable(arg: 1, scope: !46, type: !22)
// CHECK:STDOUT: !52 = !DILocation(line: 23, column: 46, scope: !46)
// CHECK:STDOUT: !52 = !DILocation(line: 23, column: 38, scope: !46)
// CHECK:STDOUT: !53 = distinct !DISubprogram(name: "Next", linkageName: "_CNext.e163a239f9fa927c:Iterate.Core.620427d555024fe2", scope: null, file: !47, line: 24, type: !54, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !56)
// CHECK:STDOUT: !54 = !DISubroutineType(types: !55)
// CHECK:STDOUT: !55 = !{!22, !22, !22}
@@ -360,4 +360,4 @@ fn F() {
// CHECK:STDOUT: !133 = distinct !DISubprogram(name: "AsInt", linkageName: "_CAsInt.Int.9452f4c51951679b.Core:AnyInt.Core.be1e879c1ad406d8", scope: null, file: !82, line: 58, type: !127, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !134)
// CHECK:STDOUT: !134 = !{!135}
// CHECK:STDOUT: !135 = !DILocalVariable(arg: 1, scope: !133, type: !15)
// CHECK:STDOUT: !136 = !DILocation(line: 58, column: 36, scope: !133)
// CHECK:STDOUT: !136 = !DILocation(line: 58, column: 28, scope: !133)