Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
faaddb1
feat(stdlib): typed Result errors across os, time, decimal, strings, …
Azer0s May 9, 2026
f956609
feat(adt,try): traits on data, try keyword, call-site generics via wi…
Azer0s May 9, 2026
3b79582
fix(codegen): retain trait data on return to prevent premature free
Azer0s May 10, 2026
3096794
refactor(stdlib): collapse typed error wrappers into errors::Err Result
Azer0s May 10, 2026
1c806a8
feat(adt,await): traits on data, Result-based net/sync, awaitable rework
Azer0s May 10, 2026
9bc19a2
feat(adt): rework ADTs with traits, try keyword, Result-based stdlib,…
Azer0s May 11, 2026
9b626e9
docs: write up try / fn main() Result and showcase try in the README
Azer0s May 12, 2026
6ac5294
docs: update README with detailed benchmark results and analysis
Azer0s May 12, 2026
d23f95f
feat(compiler): pipe-to-method, fix match+short-circuit dominance, ex…
Azer0s May 12, 2026
cc107fc
refactor(stdlib): drop top-level Result/Option helpers superseded by …
Azer0s May 12, 2026
e4e89d5
fix(codegen): substitute generic type params through fn-typed signatures
Azer0s May 12, 2026
0552a47
feat: value-form errors::Err with ARC-correct trait fat-ptr handling
Azer0s May 12, 2026
bbbbd63
fix(codegen): balance iface RC across ADT ctors, try-expr, and match …
Azer0s May 12, 2026
542c3fb
fix(os): fast-fail spawn on missing absolute path so leaks doesn't hang
Azer0s May 12, 2026
dac0ff7
fix(codegen): walk TryExpr inner so used pkg isn't flagged unused
Azer0s May 12, 2026
1ca866b
chore: drop unused imports + silence intentional must-use in tests
Azer0s May 12, 2026
964c2b8
feat(testrunner): //!-suppressions= directive for per-file valgrind s…
Azer0s May 12, 2026
84c79c4
fix(testrunner): tokenize //! linker flags so -framework Foo splits (…
Azer0s May 12, 2026
64bd224
docs(interop): document multi-token //! flags and //!-suppressions=
Azer0s May 12, 2026
e00110d
fix(codegen): reject ADT-as-tuple destructure; migrate stale call sites
Azer0s May 12, 2026
f8de284
fix(codegen): iface/array ARC leaks + Future-returning Channel/Cond
Azer0s May 13, 2026
67b46f4
fix(codegen): restore channel fast path through outer Future wrapper
Azer0s May 13, 2026
0379bb1
feat(test): color ok/FAIL, summary, and memcheck output in tty mode
Azer0s May 13, 2026
6114490
fix(lint): satisfy wsl_v5 spacing in test color path
Azer0s May 13, 2026
96ca613
fix(lint): misspellings, unused walkRCStructFields, wsl_v5 spacing
Azer0s May 13, 2026
483993f
fix(linker): dedup -framework as (flag, value) pairs to keep them tog…
Azer0s May 13, 2026
81cecab
fix(lint): wsl_v5 spacing in dedupLinkerFlags + call sites
Azer0s May 13, 2026
cc4942b
fix(lint): blank line before append in dedup pair branch
Azer0s May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 0 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -264,29 +264,3 @@ jobs:

- name: Run stdlib tests
run: ./tin test stdlib/...

- name: Run shell test scripts
run: |
for f in \
examples/compiler_errors/test_errors.sh \
examples/const_fields_errors.sh \
examples/ctfe_stress_dispatch.sh \
examples/ctfe_stress_ir_checks.sh \
examples/escape_promotion.sh \
examples/incremental_cache_verify.sh \
examples/interop_errors.sh \
examples/maranget_exhaustive.sh \
examples/method_where_errors.sh \
examples/operator_errors.sh \
examples/operator_overload_phase0.sh \
examples/pattern_where_errors.sh \
examples/pure_fold_ir_checks.sh \
examples/pure_soundness_errors.sh \
examples/scoped_struct_tags_errors.sh \
examples/trait_coercion_errors.sh \
examples/unreachable_arm_warnings.sh \
examples/interop_closure_returns/run.sh \
examples/interop_stacktrace/run.sh; do
echo "=== $f ==="
bash "$f"
done
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ core.*
vgcore.*
massif.out.*

# Working notes
notes/

# IDE / editor
/utils/tin-intellij/.kotlin/
.idea
Expand Down
50 changes: 38 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,37 @@ fn fib(n u32) u32 =

echo fib(10)

// Result + `try`: parse, propagate, done.
use errors
use { Result } from result
use strings

fn parse_pair(s string) Result[(i64, i64), errors::Err] =
let parts = strings::split(s, ",")

if len(parts) != 2:
return Err(errors::new("need two comma-separated ints"))

let a = try strings::parse_int(parts[0])
let b = try strings::parse_int(parts[1])

return Ok((a, b))

fn main() Result[Unit, errors::Err] =
let (x, y) = try parse_pair("12,30")

echo "{x} + {y} = {x + y}"

return Ok(())

// Structs with methods
struct person =
struct Person =
name string
age u8

fn show(this person) string = return "{this.name} is {this.age}"
fn show(this Person) string = return "{this.name} is {this.age}"

let pete = person{name: "Pete", age: 20}
let pete = Person{name: "Pete", age: 20}
echo pete.show()

// Fibers and channels
Expand Down Expand Up @@ -57,27 +80,30 @@ go build -o tin .
./tin run examples/hello.tin
```

That's it. There are no install targets; treat the `tin` binary as
relocatable and put it on your `PATH` if you want.
That's the whole install: no install target, no package manager, no
project file. The `tin` binary is relocatable; drop it on your `PATH` if
you want.

### Common commands

```sh
tin run file.tin # compile and execute
tin build file.tin -o out # produce a native binary
tin build --lib file.tin # produce a relocatable .o
tin test path/... # run all `test` blocks under path/, recursive
tin test path/... # run every `test` block under path/, recursive
tin repl # interactive REPL (preload with `tin repl file.tin`)
tin clean # wipe the .build/ cache
```

`tin run` and `tin test` content-hash every input file (entry source,
imported packages, `//!+` C sources) and skip lex/parse/codegen entirely
when the cache directory's `sbom.txt` matches. Cache lives at
`.build/<run|test>/<file>_<md5>/`.
`tin run` and `tin test` content-hash every input (entry source,
imported packages, `//!+` C sources) and skip the entire lex / parse /
codegen pipeline when the cache directory's `sbom.txt` matches. The
cache lives at `.build/<run|test>/<file>_<md5>/` -- delete it manually
with `tin clean` or just `rm -rf .build/`.

For tighter loops on the test suite: `tin test --fast path/...` drops to
`-O0` so LLVM's optimizer doesn't dominate compile time.
For tight inner loops on the test suite: `tin test --fast path/...`
drops to `-O0` so LLVM's optimizer stops dominating the wall-clock
compile time.

## Dependencies

Expand Down
62 changes: 62 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ type FuncDecl struct {
IsStatic bool
IsExtern string // non-empty = extern symbol name
IsVirtual bool // true for "fn f() T = virtual" in trait declarations

// RetTypeHasWildcard records whether RetType contained a WildcardType
// before generic substitution. Preserved through monomorphization so
// call sites can opt into wildcard-driven coercion ("call-site
// generics") for the return value.
RetTypeHasWildcard bool
}

// TypeConstraint bounds a type parameter with a boolean expression of trait
Expand Down Expand Up @@ -138,6 +144,7 @@ type StructDecl struct {
base
Name string
TypeParams []string
Wildcards []string // call-site-supplied wildcard slot names (e.g. `_: W` in `struct Foo[T, _: W]`)
Constraints []TypeConstraint // generic type constraints: where t is addable
Fields []StructField
Methods []*FuncDecl
Expand Down Expand Up @@ -201,12 +208,25 @@ type UnionDecl struct {
// Each variant carries zero or more positional or named fields.
// At least one variant must carry a payload (pure-nullary shapes should
// use `enum` instead).
//
// ADTs may also implement traits, mirroring struct trait impls:
//
// data Result[T, E](tryable[T, Result[T, E]]) =
// Ok(v T)
// Err(msg E)
//
// fn tryable[T, Result[T, E]]::is_err(this Result[T, E]) bool = ...
//
// Trait impls live in the Implements list; method bodies live in Methods.
type DataDecl struct {
base
Name string
TypeParams []string
Wildcards []string // call-site-supplied wildcard slot names (e.g. `_: W` in `data Foo[T, _: W]`)
Constraints []TypeConstraint
Variants []DataVariant
Implements []TypeExpr // trait impls listed in parens, mirrors StructDecl.Implements
Methods []*FuncDecl // method bodies for trait impls, mirrors StructDecl.Methods
}

type DataVariant struct {
Expand Down Expand Up @@ -525,6 +545,16 @@ type AsExpr struct {
Type TypeExpr
}

// TypeRefNode lifts a TypeExpr into expression position. Used so the parser
// can place a complex type (e.g. `fn(i64) i64`) inside an IndexExpr's Index
// slot when the brackets are being used for type-arg instantiation rather
// than runtime indexing. Downstream resolvers (exprToTypeParamKey,
// typeNodeToString, typeNodeToTypeExpr) unwrap the carried TypeExpr.
type TypeRefNode struct {
base
Type TypeExpr
}

type SizeofExpr struct {
base
Type TypeExpr
Expand Down Expand Up @@ -876,6 +906,20 @@ type AwaitExpr struct {
Future Node
}

// TryExpr propagates a fallible-typed expression: `try foo()`.
// Codegen desugars to:
//
// let __t = expr
// if __t.tryable::is_err():
// return __t.tryable::err_value()
// __t.tryable::ok_value()
//
// The enclosing fn must return a type compatible with err_value's return.
type TryExpr struct {
base
Inner Node
}

// YieldStmt voluntarily yields the current fiber's time slice.
type YieldStmt struct{ base }

Expand Down Expand Up @@ -1003,3 +1047,21 @@ type VoidType struct{}

func (v *VoidType) typeExprMarker() {}
func (v *VoidType) String() string { return "void" }

// WildcardType is the `_` placeholder permitted only in trait-bound
// positions. It denotes an existentially-bound slot the impl picks.
// Name is empty for anonymous `_`; non-empty for `_: T` where T is the
// caller-supplied name used to refer to the slot inside the trait body
// or impl methods.
type WildcardType struct {
Name string // "" for anonymous `_`, otherwise the introduced name
}

func (w *WildcardType) typeExprMarker() {}
func (w *WildcardType) String() string {
if w.Name == "" {
return "_"
}

return "_: " + w.Name
}
Loading
Loading