feat: match guards, HOF inference, ownership C.2/C.3, LSP sema hover
Sessions 18–23 quality work: - B.3c match arm guards + sequential found-flag lower (bootstrap + selfhost) - Generic HOF type inference (Array/Iter map/filter/fold without type args) - Pattern binding shadowing via unique C locals (__pN_src) - Ownership C.2 exclusive &mut data-flow + C.4 goldens; *p= store-through fix - Ownership C.3 auto-drop on early return/branches: scoped defers, move-on-return, Drop monomorphization, materialize return before Drop - LSP 0.3.0: hover from real sema types - Examples and QUALITY_PLAN session log; selfhost-loop identical
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// C.3 — Auto-drop on early return and if branches
|
||||
// @[Drop] types call Type_Drop at every exit (return + branch scope end).
|
||||
|
||||
import Std::Io::{PrintLine, PrintInt};
|
||||
import Std::Test::{Test_AssertEqInt, Test_Pass};
|
||||
|
||||
@[Drop]
|
||||
struct Token {
|
||||
id: int,
|
||||
counter: *int
|
||||
}
|
||||
|
||||
func Token_Drop(self: *Token) {
|
||||
if self.counter != null as *int {
|
||||
*self.counter = *self.counter + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Drop t on both early return and fallthrough return.
|
||||
func Early(flag: int, counter: *int) -> int {
|
||||
let t: Token = Token { id: 1, counter: counter };
|
||||
if flag == 0 {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Branch-local Tokens: only the taken branch's Drop runs.
|
||||
func Branched(flag: int, counter: *int) -> int {
|
||||
if flag == 1 {
|
||||
let a: Token = Token { id: 10, counter: counter };
|
||||
return a.id;
|
||||
} else {
|
||||
let b: Token = Token { id: 20, counter: counter };
|
||||
return b.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallthrough: Drop at end of block without return.
|
||||
func Scoped(counter: *int) {
|
||||
let s: Token = Token { id: 99, counter: counter };
|
||||
discard s.id;
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
var drops: int = 0;
|
||||
discard Early(0, &drops);
|
||||
discard Early(1, &drops);
|
||||
Test_AssertEqInt(drops, 2);
|
||||
|
||||
discard Branched(1, &drops);
|
||||
discard Branched(0, &drops);
|
||||
Test_AssertEqInt(drops, 4);
|
||||
|
||||
Scoped(&drops);
|
||||
Test_AssertEqInt(drops, 5);
|
||||
|
||||
PrintInt(drops);
|
||||
PrintLine("");
|
||||
Test_Pass("drop_early_return");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Generic HOF type inference — Iter_Map / Filter / Fold without explicit <T,U>
|
||||
// Also Array_Push / Array_Get / Array_Iter inferred from arguments.
|
||||
import Std::Io::{PrintLine, PrintInt};
|
||||
import Std::Array::{
|
||||
Array, Array_New, Array_Push, Array_Get, Array_Len, Array_Free
|
||||
};
|
||||
import Std::String::{String_FromInt, String_Len, String_Eq};
|
||||
import Std::Iter::{
|
||||
Array_Iter, Iter,
|
||||
Iter_Map, Iter_Filter, Iter_Fold, Iter_Any, Iter_All
|
||||
};
|
||||
import Std::Test::{
|
||||
Test_AssertEqInt, Test_AssertTrue, Test_AssertEqString, Test_Pass
|
||||
};
|
||||
|
||||
func Double(x: int) -> int {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
func IsEven(x: int) -> bool {
|
||||
return (x % 2) == 0;
|
||||
}
|
||||
|
||||
func IntToString(x: int) -> String {
|
||||
return String_FromInt(x);
|
||||
}
|
||||
|
||||
func StringLenAsInt(s: String) -> int {
|
||||
return String_Len(s) as int;
|
||||
}
|
||||
|
||||
func AddLens(acc: int, s: String) -> int {
|
||||
return acc + (String_Len(s) as int);
|
||||
}
|
||||
|
||||
func AddInt(acc: int, x: int) -> int {
|
||||
return acc + x;
|
||||
}
|
||||
|
||||
func IsNonEmpty(s: String) -> bool {
|
||||
return String_Len(s) > 0;
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
var nums: Array<int> = Array_New<int>(8);
|
||||
// Type args inferred from &nums : *Array<int> and value type
|
||||
Array_Push(&nums, 1);
|
||||
Array_Push(&nums, 2);
|
||||
Array_Push(&nums, 3);
|
||||
Array_Push(&nums, 4);
|
||||
Array_Push(&nums, 5);
|
||||
|
||||
// Iter_Map<int,int> inferred from *Iter<int> + func(int)->int
|
||||
let itA: Iter<int> = Array_Iter(&nums);
|
||||
var doubled: Array<int> = Iter_Map(&itA, Double);
|
||||
Test_AssertEqInt(Array_Get(&doubled, 0), 2);
|
||||
Test_AssertEqInt(Array_Get(&doubled, 4), 10);
|
||||
|
||||
// Iter_Map<int,String>
|
||||
let itB: Iter<int> = Array_Iter(&nums);
|
||||
var asStr: Array<String> = Iter_Map(&itB, IntToString);
|
||||
Test_AssertEqInt(Array_Len(&asStr) as int, 5);
|
||||
Test_AssertEqString(Array_Get(&asStr, 0), "1");
|
||||
Test_AssertEqString(Array_Get(&asStr, 4), "5");
|
||||
|
||||
// Iter_Map<String,int>
|
||||
let itC: Iter<String> = Array_Iter(&asStr);
|
||||
var lens: Array<int> = Iter_Map(&itC, StringLenAsInt);
|
||||
Test_AssertEqInt(Array_Get(&lens, 0), 1);
|
||||
|
||||
// Iter_Filter<int>
|
||||
let itD: Iter<int> = Array_Iter(&nums);
|
||||
var evens: Array<int> = Iter_Filter(&itD, IsEven);
|
||||
Test_AssertEqInt(Array_Len(&evens) as int, 2);
|
||||
Test_AssertEqInt(Array_Get(&evens, 0), 2);
|
||||
Test_AssertEqInt(Array_Get(&evens, 1), 4);
|
||||
|
||||
// Iter_Fold<String,int> — Acc from init, T from *Iter + func
|
||||
let itE: Iter<String> = Array_Iter(&asStr);
|
||||
let totalChars: int = Iter_Fold(&itE, 0, AddLens);
|
||||
Test_AssertEqInt(totalChars, 5);
|
||||
|
||||
let itF: Iter<int> = Array_Iter(&nums);
|
||||
let sum: int = Iter_Fold(&itF, 0, AddInt);
|
||||
Test_AssertEqInt(sum, 15);
|
||||
|
||||
let itG: Iter<String> = Array_Iter(&asStr);
|
||||
Test_AssertTrue(Iter_Any(&itG, IsNonEmpty));
|
||||
|
||||
PrintInt(sum);
|
||||
PrintLine("");
|
||||
Test_Pass("generic_infer_hof");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Match arm guards: `p if cond => body`
|
||||
// Bindings from the pattern are in scope for the guard expression.
|
||||
import Std::Io::{PrintLine, PrintInt};
|
||||
import Std::Test::{Test_AssertEqInt, Test_Pass};
|
||||
|
||||
enum Option {
|
||||
Some(int),
|
||||
None
|
||||
}
|
||||
|
||||
func Classify(n: int) -> int {
|
||||
return match n {
|
||||
x if x < 0 => -1,
|
||||
x if x == 0 => 0,
|
||||
x if x > 0 => 1,
|
||||
_ => 99
|
||||
};
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
// Ident + guard
|
||||
Test_AssertEqInt(Classify(-5), -1);
|
||||
Test_AssertEqInt(Classify(0), 0);
|
||||
Test_AssertEqInt(Classify(7), 1);
|
||||
|
||||
// Literal + guard (second arm only when n is even)
|
||||
let n: int = 4;
|
||||
let a: int = match n {
|
||||
4 if n % 2 == 0 => 40,
|
||||
4 => 41,
|
||||
_ => -1
|
||||
};
|
||||
Test_AssertEqInt(a, 40);
|
||||
|
||||
let m: int = 4;
|
||||
let b: int = match m {
|
||||
4 if m % 2 != 0 => 40,
|
||||
4 => 41,
|
||||
_ => -1
|
||||
};
|
||||
Test_AssertEqInt(b, 41);
|
||||
|
||||
// Range + guard
|
||||
let k: int = 5;
|
||||
let c: int = match k {
|
||||
1..10 if k % 2 == 0 => 100,
|
||||
1..10 => 101,
|
||||
_ => -1
|
||||
};
|
||||
Test_AssertEqInt(c, 101);
|
||||
|
||||
let j: int = 6;
|
||||
let d: int = match j {
|
||||
1..10 if j % 2 == 0 => 100,
|
||||
1..10 => 101,
|
||||
_ => -1
|
||||
};
|
||||
Test_AssertEqInt(d, 100);
|
||||
|
||||
// Enum payload binding used in guard
|
||||
let opt: Option = Option { tag: Option_Some };
|
||||
opt.data.Some_0 = 15;
|
||||
let e: int = match opt {
|
||||
Option::Some(v) if v > 10 => v * 2,
|
||||
Option::Some(v) => v,
|
||||
Option::None => 0
|
||||
};
|
||||
Test_AssertEqInt(e, 30);
|
||||
|
||||
let small: Option = Option { tag: Option_Some };
|
||||
small.data.Some_0 = 3;
|
||||
let f: int = match small {
|
||||
Option::Some(v) if v > 10 => v * 2,
|
||||
Option::Some(v) => v,
|
||||
Option::None => 0
|
||||
};
|
||||
Test_AssertEqInt(f, 3);
|
||||
|
||||
PrintInt(e);
|
||||
PrintLine("");
|
||||
Test_Pass("match_guards");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Std::Io::{PrintLine, PrintInt};
|
||||
import Std::Test::{Test_AssertEqInt, Test_Pass};
|
||||
|
||||
func UncheckedInc(p: *int) {
|
||||
*p = *p + 1;
|
||||
}
|
||||
|
||||
@[Checked]
|
||||
func Inc(p: &mut int) {
|
||||
*p = *p + 1;
|
||||
}
|
||||
|
||||
@[Checked]
|
||||
func Get(p: &int) -> int {
|
||||
return *p;
|
||||
}
|
||||
|
||||
@[Checked]
|
||||
func OkBorrow() -> int {
|
||||
var x: int = 10;
|
||||
Inc(&x);
|
||||
return Get(&x);
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
var n: int = 1;
|
||||
UncheckedInc(&n);
|
||||
Test_AssertEqInt(n, 2);
|
||||
let r: int = OkBorrow();
|
||||
Test_AssertEqInt(r, 11);
|
||||
PrintInt(r);
|
||||
PrintLine("");
|
||||
Test_Pass("ownership_checked");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import Std::Test::{Test_AssertEqInt, Test_Pass};
|
||||
import Std::Io::{PrintInt, PrintLine};
|
||||
|
||||
enum Option {
|
||||
Some(int),
|
||||
None
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
let a: Option = Option { tag: Option_Some };
|
||||
a.data.Some_0 = 10;
|
||||
let b: Option = Option { tag: Option_Some };
|
||||
b.data.Some_0 = 20;
|
||||
|
||||
// Same binding name `v` in two sequential matches
|
||||
let x: int = match a {
|
||||
Option::Some(v) => v + 1,
|
||||
Option::None => 0
|
||||
};
|
||||
let y: int = match b {
|
||||
Option::Some(v) => v + 2,
|
||||
Option::None => 0
|
||||
};
|
||||
Test_AssertEqInt(x, 11);
|
||||
Test_AssertEqInt(y, 22);
|
||||
|
||||
// Nested: outer and inner both bind `n`
|
||||
let nested: int = match a {
|
||||
Option::Some(n) => match b {
|
||||
Option::Some(n) => n, // should be b's payload (20), not a's
|
||||
Option::None => -1
|
||||
},
|
||||
Option::None => -2
|
||||
};
|
||||
Test_AssertEqInt(nested, 20);
|
||||
|
||||
// Shadow outer let with pattern binding
|
||||
let v: int = 99;
|
||||
let z: int = match a {
|
||||
Option::Some(v) => v, // pattern v should be 10, not 99
|
||||
Option::None => 0
|
||||
};
|
||||
Test_AssertEqInt(z, 10);
|
||||
Test_AssertEqInt(v, 99); // outer v unchanged
|
||||
|
||||
PrintInt(nested);
|
||||
PrintLine("");
|
||||
Test_Pass("pat_shadow");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user