Skip to content

Commit f1b0977

Browse files
committed
Add support for named return values. Modify AST and code generation to accommodate new return type specifications.
1 parent 2e504cc commit f1b0977

20 files changed

Lines changed: 981 additions & 105 deletions

SPEC.md

Lines changed: 135 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,21 +2356,14 @@ value = (value % modulus); // From: value %= modulus
23562356

23572357
## 7. Functions and Control Flow
23582358

2359-
### 7.1 Function Declaration
2360-
```ebnf
2361-
function_declaration = [ attribute_list ] [ visibility ] [ "kernel" ] "fn" identifier "(" parameter_list ")" [ "->" return_type ] "{" statement_list "}"
2359+
### 7.1 Function Declaration Overview
23622360

2363-
attribute_list = attribute { attribute }
2364-
attribute = "@" attribute_name [ "(" attribute_args ")" ]
2365-
attribute_name = "xdp" | "tc" | "kprobe" | "uprobe" | "tracepoint" |
2366-
"lsm" | "cgroup_skb" | "socket_filter" | "sk_lookup" | "struct_ops" | "kfunc"
2367-
attribute_args = string_literal | identifier
2361+
KernelScript functions support both traditional unnamed return types and modern named return values. The complete grammar is defined in Section 15 (Complete Formal Grammar).
23682362

2369-
visibility = "pub" | "priv"
2370-
parameter_list = [ parameter { "," parameter } ]
2371-
parameter = identifier ":" type_annotation
2372-
return_type = type_annotation
2373-
```
2363+
Key function types:
2364+
- **eBPF program functions**: Attributed with `@xdp`, `@tc`, etc. - compile to eBPF bytecode
2365+
- **Helper functions**: Attributed with `@helper` - shared across all eBPF programs
2366+
- **Userspace functions**: No attributes - compile to native executable
23742367

23752368
### 7.2 eBPF Program Functions
23762369
```kernelscript
@@ -2387,7 +2380,127 @@ fn simple_xdp(ctx: *xdp_md) -> xdp_action {
23872380
}
23882381
```
23892382

2390-
### 7.3 Helper Functions
2383+
### 7.3 Named Return Values
2384+
2385+
KernelScript supports both unnamed and named return values following Go's syntax pattern:
2386+
2387+
- **Unnamed returns** (backward compatible): `fn name() -> type`
2388+
- **Named returns** (new): `fn name() -> var_name: type`
2389+
2390+
Named return values automatically declare a local variable with the specified name and type. This variable can be used throughout the function, and naked returns (`return` without a value) will return the current value of the named variable.
2391+
2392+
#### 7.3.1 Named Return Syntax Examples
2393+
2394+
```kernelscript
2395+
// Backward compatible unnamed return (unchanged)
2396+
fn add_numbers(a: i32, b: i32) -> i32 {
2397+
return a + b
2398+
}
2399+
2400+
// Named return value - 'sum' becomes a local variable
2401+
fn add_numbers_named(a: i32, b: i32) -> sum: i32 {
2402+
sum = a + b // Named variable is automatically declared
2403+
return // Naked return - returns current value of 'sum'
2404+
}
2405+
2406+
// Using named return in complex logic
2407+
fn calculate_hash(data: *u8, len: u32) -> hash_value: u64 {
2408+
hash_value = 0 // Named return variable is available immediately
2409+
2410+
for (i in 0..len) {
2411+
hash_value = hash_value * 31 + data[i] // Modify throughout function
2412+
}
2413+
2414+
return // Naked return with computed hash_value
2415+
}
2416+
2417+
// Mixing named variables with explicit returns
2418+
fn validate_packet(data: *u8, len: u32) -> is_valid: bool {
2419+
is_valid = false // Start with default value
2420+
2421+
if (len == 0) {
2422+
return // Early naked return with is_valid = false
2423+
}
2424+
2425+
if (data == null) {
2426+
return false // Explicit return still works
2427+
}
2428+
2429+
is_valid = true // Set to true if all checks pass
2430+
return // Final naked return
2431+
}
2432+
```
2433+
2434+
#### 7.3.2 Named Returns in Different Contexts
2435+
2436+
Named return values work consistently across all function types:
2437+
2438+
```kernelscript
2439+
// eBPF helper functions with named returns
2440+
@helper
2441+
fn extract_ip_header(ctx: *xdp_md) -> ip_hdr: *iphdr {
2442+
var data = ctx->data
2443+
var data_end = ctx->data_end
2444+
2445+
if (data + 14 + 20 > data_end) {
2446+
ip_hdr = null
2447+
return // Naked return with null
2448+
}
2449+
2450+
ip_hdr = (iphdr*)(data + 14)
2451+
return // Naked return with pointer
2452+
}
2453+
2454+
// eBPF program functions with named returns
2455+
@xdp
2456+
fn packet_filter(ctx: *xdp_md) -> action: xdp_action {
2457+
action = XDP_PASS // Default action
2458+
2459+
var size = ctx->data_end - ctx->data
2460+
if (size < 64) {
2461+
action = XDP_DROP
2462+
return // Naked return with XDP_DROP
2463+
}
2464+
2465+
return // Naked return with XDP_PASS
2466+
}
2467+
2468+
// Userspace functions with named returns
2469+
fn lookup_counter(ip: u32) -> counter_ptr: *u64 {
2470+
if (counters[ip] == none) {
2471+
counters[ip] = 0
2472+
}
2473+
counter_ptr = &counters[ip]
2474+
return // Naked return
2475+
}
2476+
2477+
// Function pointer types with named returns
2478+
type HashFunction = fn(*u8, u32) -> hash: u64
2479+
type PacketProcessor = fn(*xdp_md) -> result: xdp_action
2480+
```
2481+
2482+
#### 7.3.3 Code Generation
2483+
2484+
Named return values compile to clean, efficient C code with zero runtime overhead:
2485+
2486+
**KernelScript:**
2487+
```kernelscript
2488+
fn calculate_sum(a: i32, b: i32) -> result: i32 {
2489+
result = a + b
2490+
return
2491+
}
2492+
```
2493+
2494+
**Generated C:**
2495+
```c
2496+
static int calculate_sum(int a, int b) {
2497+
int result; // Named return variable declared
2498+
result = a + b;
2499+
return result; // Naked return becomes explicit
2500+
}
2501+
```
2502+
2503+
### 7.4 Helper Functions
23912504
23922505
KernelScript supports two types of functions with different scoping rules:
23932506
@@ -2463,7 +2576,7 @@ fn main() -> i32 {
24632576
}
24642577
```
24652578

2466-
### 7.4 eBPF Tail Calls
2579+
### 7.5 eBPF Tail Calls
24672580

24682581
KernelScript provides transparent eBPF tail call support that automatically converts function calls to tail calls when appropriate. Tail calls enable efficient program chaining without stack overhead and are especially useful for packet processing pipelines.
24692582

@@ -4149,11 +4262,15 @@ type_alias = type_annotation
41494262
41504263
(* Function declarations *)
41514264
function_declaration = [ attribute_list ] [ visibility ] [ "kernel" ] "fn" identifier "(" parameter_list ")"
4152-
[ "->" type_annotation ] "{" statement_list "}"
4265+
[ return_type_spec ] "{" statement_list "}"
4266+
4267+
(* Return type specification - supports both unnamed and named return values *)
4268+
return_type_spec = "->" type_annotation (* Unnamed: fn() -> u64 *)
4269+
| "->" identifier ":" type_annotation (* Named: fn() -> result: u64 *)
41534270
41544271
impl_declaration = [ attribute_list ] "impl" identifier "{" impl_body "}"
41554272
impl_body = { impl_function }
4156-
impl_function = "fn" identifier "(" parameter_list ")" [ "->" type_annotation ] "{" statement_list "}"
4273+
impl_function = "fn" identifier "(" parameter_list ")" [ return_type_spec ] "{" statement_list "}"
41574274
41584275
visibility = "pub" | "priv"
41594276
parameter_list = [ parameter { "," parameter } ]
@@ -4252,7 +4369,7 @@ string_type = "str" "(" integer_literal ")"
42524369
42534370
array_type = "[" type_annotation "" integer_literal "]"
42544371
pointer_type = "*" type_annotation
4255-
function_type = "fn" "(" [ type_annotation { "," type_annotation } ] ")" [ "->" type_annotation ]
4372+
function_type = "fn" "(" [ type_annotation { "," type_annotation } ] ")" [ return_type_spec ]
42564373
42574374
(* Literals *)
42584375
literal = integer_literal | string_literal | char_literal | boolean_literal |

examples/named_return.ks

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// XDP context struct (from BTF)
2+
struct xdp_md {
3+
data: u64,
4+
data_end: u64,
5+
data_meta: u64,
6+
ingress_ifindex: u32,
7+
rx_queue_index: u32,
8+
egress_ifindex: u32,
9+
}
10+
11+
// XDP action enum (from BTF)
12+
enum xdp_action {
13+
XDP_ABORTED = 0,
14+
XDP_DROP = 1,
15+
XDP_PASS = 2,
16+
XDP_REDIRECT = 3,
17+
XDP_TX = 4,
18+
}
19+
20+
// Basic named return value - Go-style syntax
21+
fn add_with_named_return(a: i32, b: i32) -> sum: i32 {
22+
sum = a + b // 'sum' is automatically declared as a local variable
23+
return // Naked return - returns current value of 'sum'
24+
}
25+
26+
// Named return with complex logic
27+
fn calculate_hash(value: u32, multiplier: u32) -> hash_value: u64 {
28+
hash_value = 0 // Named return variable is available immediately
29+
30+
for (i in 0..multiplier) {
31+
hash_value = hash_value * 31 + value // Modify throughout function
32+
}
33+
34+
return // Naked return with computed hash_value
35+
}
36+
37+
// Mixing named variables with explicit returns
38+
fn validate_length(len: u32, min_len: u32) -> is_valid: bool {
39+
is_valid = false // Start with default value
40+
41+
if (len == 0) {
42+
return // Early naked return with is_valid = false
43+
}
44+
45+
if (len < min_len) {
46+
return false // Explicit return still works
47+
}
48+
49+
is_valid = true // Set to true if all checks pass
50+
return // Final naked return
51+
}
52+
53+
// eBPF helper functions with named returns
54+
@helper
55+
fn calculate_packet_size(ctx: *xdp_md) -> packet_size: u32 {
56+
var data = ctx->data
57+
var data_end = ctx->data_end
58+
59+
if (data_end <= data) {
60+
packet_size = 0
61+
return // Naked return with 0
62+
}
63+
64+
packet_size = data_end - data // Calculate size
65+
return // Naked return with size
66+
}
67+
68+
// eBPF program functions with named returns
69+
@xdp
70+
fn advanced_packet_filter(ctx: *xdp_md) -> action: xdp_action {
71+
action = XDP_PASS // Default action
72+
73+
var size = ctx->data_end - ctx->data
74+
if (size < 64) {
75+
action = XDP_DROP
76+
return // Naked return with XDP_DROP
77+
}
78+
79+
var packet_size = calculate_packet_size(ctx)
80+
if (packet_size == 0) {
81+
action = XDP_ABORTED
82+
return // Naked return with XDP_ABORTED
83+
}
84+
85+
return // Naked return with XDP_PASS
86+
}
87+
88+
// Userspace functions with named returns
89+
fn lookup_counter(ip: u32) -> counter_value: u64 {
90+
// This would normally access a map, simplified for example
91+
counter_value = ip * 1000 // Compute some value
92+
93+
if (counter_value > 1000000) {
94+
counter_value = 0 // Reset if too high
95+
}
96+
97+
return // Naked return
98+
}
99+
100+
type HashFunction = fn(*u8, u32) -> u64
101+
type PacketProcessor = fn(*xdp_md) -> xdp_action
102+
103+
// Example with recursive named returns
104+
fn fibonacci(n: u32) -> result: u64 {
105+
if (n <= 1) {
106+
result = n
107+
return
108+
}
109+
110+
var a = fibonacci(n - 1)
111+
var b = fibonacci(n - 2)
112+
result = a + b
113+
return
114+
}
115+
116+
// Named return with error handling
117+
fn safe_divide(numerator: i32, denominator: i32) -> quotient: i32 {
118+
if (denominator == 0) {
119+
quotient = 0 // Safe default
120+
return
121+
}
122+
123+
quotient = numerator / denominator
124+
return
125+
}
126+
127+
// Complex example combining multiple features
128+
fn process_data_with_validation(value: u32, len: u32) -> status: i32 {
129+
status = -1 // Error by default
130+
131+
// Validate input
132+
if (value == 0 || len == 0) {
133+
return // Early return with error status
134+
}
135+
136+
// Calculate hash for validation
137+
var hash = calculate_hash(value, len)
138+
if (hash == 0) {
139+
status = -2 // Invalid hash
140+
return
141+
}
142+
143+
// Process successful
144+
status = 0
145+
return
146+
}
147+
148+
fn main() -> exit_code: i32 {
149+
print("=== Named Return Values Demo ===")
150+
151+
// Demonstrate basic named return
152+
var sum = add_with_named_return(10, 20)
153+
print("Sum with named return: %d", sum)
154+
155+
// Test validation function
156+
var validation_result = validate_length(25, 10)
157+
if (validation_result) {
158+
print("Validation result: valid")
159+
} else {
160+
print("Validation result: invalid")
161+
}
162+
163+
// Test hash calculation
164+
var hash = calculate_hash(42, 5)
165+
print("Hash value: %llu", hash)
166+
167+
// Test counter lookup
168+
var counter = lookup_counter(0x08080808) // Google DNS
169+
print("Counter for 8.8.8.8: %llu", counter)
170+
171+
// Test fibonacci
172+
var fib_result = fibonacci(10)
173+
print("Fibonacci(10) = %llu", fib_result)
174+
175+
// Test safe division
176+
var quotient1 = safe_divide(10, 2)
177+
var quotient2 = safe_divide(10, 0) // Safe division by zero
178+
print("10 / 2 = %d, 10 / 0 = %d", quotient1, quotient2)
179+
180+
// Test complex processing
181+
var status = process_data_with_validation(123, 10)
182+
print("Processing status: %d", status)
183+
184+
print("=== Demo Complete ===")
185+
186+
exit_code = 0 // Set named return variable
187+
return // Naked return with exit_code = 0
188+
}

0 commit comments

Comments
 (0)