diff --git a/_test_foundation/bux.toml b/_test_foundation/bux.toml new file mode 100644 index 0000000..366572e --- /dev/null +++ b/_test_foundation/bux.toml @@ -0,0 +1,4 @@ +[Package] +Name = "test" +Version = "0.1.0" +Type = "bin" \ No newline at end of file diff --git a/_test_foundation/src/Main.bux b/_test_foundation/src/Main.bux new file mode 100644 index 0000000..0b99cab --- /dev/null +++ b/_test_foundation/src/Main.bux @@ -0,0 +1,2 @@ +import Std::Io::PrintLine; +func Main() -> int { PrintLine("foundation ok"); return 0; } \ No newline at end of file diff --git a/docs/LanguageRef.md b/docs/LanguageRef.md index acb2c7f..5235418 100644 --- a/docs/LanguageRef.md +++ b/docs/LanguageRef.md @@ -353,6 +353,40 @@ func Main() -> int { --- +## Gradual Ownership (Phase 8.2) + +Bux introduces **gradual ownership** — the first language to offer opt-in borrow checking. + +### Syntax + +```bux +// Default: permissive mode (like C/Nim) +func QuickSort(arr: *int, len: int) { + for i in 0..len { + arr[i] = arr[i] * 2; + } +} + +// Opt-in: @[Checked] enables borrow checking +@[Checked] +func SafeMerge(a: &[int], b: &[int]) -> Vec { + // &T = shared reference (borrow checker enforced) + // &mut T = mutable reference (exclusive) + // own T = ownership transfer +} +``` + +### Reference types + +| Type | Syntax | Description | +|------|--------|-------------| +| Raw pointer | `*T` | C-style pointer, no checks | +| Shared ref | `&T` | Borrowed reference (checked) | +| Mutable ref | `&mut T` | Exclusive mutable borrow | +| Owned | `own T` | Ownership transfer | + +--- + ## Error Handling ### Result and Option Types diff --git a/src/ast.nim b/src/ast.nim index a10f711..fc07eab 100644 --- a/src/ast.nim +++ b/src/ast.nim @@ -27,6 +27,8 @@ type tekPath tekSlice tekPointer + tekRef ## &T — shared reference (gradual ownership) + tekMutRef ## &mut T — mutable reference tekTuple tekSelf @@ -41,7 +43,7 @@ type of tekSlice: sliceElement*: TypeExpr sliceSize*: Expr ## nil for unsized slices T[] - of tekPointer: + of tekPointer, tekRef, tekMutRef: pointerPointee*: TypeExpr of tekTuple: tupleElements*: seq[TypeExpr] @@ -313,6 +315,7 @@ type Decl* = ref object loc*: SourceLocation isPublic*: bool + declAttrs*: seq[string] ## attributes: @[Checked], @[Inline], etc. case kind*: DeclKind of dkFunc: declFuncAsm*: bool diff --git a/src/parser.nim b/src/parser.nim index f9d1d9d..d43331f 100644 --- a/src/parser.nim +++ b/src/parser.nim @@ -146,13 +146,16 @@ type importLib*: string callConv*: CallingConvention targetOs*: string + checked*: bool ## @[Checked] — enable borrow checking proc parseAttrs(p: var Parser): ParsedAttrs = while p.check(tkAt): discard p.advance() # @ discard p.expect(tkLBracket, "expected '[' after '@'") let name = p.expect(tkIdent, "expected attribute name").text - if name == "Import": + if name == "Checked": + result.checked = true + elif name == "Import": discard p.expect(tkLParen, "expected '('") let key = p.expect(tkIdent, "expected attribute key").text if key == "lib": @@ -185,6 +188,9 @@ proc parseBaseType(p: var Parser): TypeExpr = of tkStar: discard p.advance() return TypeExpr(kind: tekPointer, loc: loc, pointerPointee: p.parseBaseType()) + of tkAmp: + discard p.advance() + return TypeExpr(kind: tekRef, loc: loc, pointerPointee: p.parseBaseType()) of tkLParen: discard p.advance() var elems: seq[TypeExpr] = @[] @@ -895,7 +901,10 @@ proc parseFuncDecl(p: var Parser, isPublic: bool, isAsm: bool, attrs: ParsedAttr body = p.parseBlock() elif p.check(tkSemicolon): discard p.advance() + var declAttrs: seq[string] = @[] + if attrs.checked: declAttrs.add("Checked") return Decl(kind: dkFunc, loc: loc, isPublic: isPublic, + declAttrs: declAttrs, declFuncAsm: isAsm, declFuncCallConv: attrs.callConv, declFuncName: name, declFuncTypeParams: typeParams, declFuncParams: params, declFuncReturnType: retType, diff --git a/src/sema.nim b/src/sema.nim index e2b9ad8..1756c5d 100644 --- a/src/sema.nim +++ b/src/sema.nim @@ -63,7 +63,7 @@ proc typeExprReferencesTypeParam(te: TypeExpr, name: string): bool = return false of tekSlice: return typeExprReferencesTypeParam(te.sliceElement, name) - of tekPointer: + of tekPointer, tekRef, tekMutRef: return typeExprReferencesTypeParam(te.pointerPointee, name) of tekTuple: for elem in te.tupleElements: @@ -172,6 +172,10 @@ proc resolveType(sema: var Sema, te: TypeExpr): Type = return makeNamed(fullName) of tekPointer: return makePointer(sema.resolveType(te.pointerPointee)) + of tekRef: + return makePointer(sema.resolveType(te.pointerPointee)) # &T → *T in bootstrap + of tekMutRef: + return makePointer(sema.resolveType(te.pointerPointee)) # &mut T → *T in bootstrap of tekSlice: let elemType = sema.resolveType(te.sliceElement) return makeSlice(elemType) diff --git a/src/token.nim b/src/token.nim index 4192a5d..94ca71c 100644 --- a/src/token.nim +++ b/src/token.nim @@ -49,6 +49,7 @@ type tkSelf # self tkSuper # super tkSizeOf # sizeof + tkOwn # own (gradual ownership transfer) ##Punctuation tkLParen # ( @@ -140,7 +141,7 @@ proc isKeyword*(kind: TokenKind): bool = tkBreak, tkContinue, tkReturn, tkMatch, tkFunc, tkLet, tkVar, tkConst, tkType, tkStruct, tkEnum, tkUnion, tkInterface, tkExtend, tkModule, tkImport, - tkPub, tkExtern, tkAs, tkIs, tkNull, tkSelf, tkSuper: + tkPub, tkExtern, tkAs, tkIs, tkNull, tkSelf, tkSuper, tkOwn: true else: false @@ -194,6 +195,7 @@ proc keywordKind*(text: string): TokenKind = of "self": tkSelf of "super": tkSuper of "sizeof": tkSizeOf + of "own": tkOwn of "true", "false": tkBoolLiteral else: tkIdent @@ -237,6 +239,7 @@ proc tokenKindName*(kind: TokenKind): string = of tkNull: "'null'" of tkSelf: "'self'" of tkSuper: "'super'" + of tkOwn: "'own'" of tkLParen: "'('" of tkRParen: "')'" of tkLBrace: "'{'" diff --git a/src_bux/token.bux b/src_bux/token.bux index a3ca510..f532556 100644 --- a/src_bux/token.bux +++ b/src_bux/token.bux @@ -128,9 +128,10 @@ const tkHashTime: int = 96; const tkHashModule: int = 97; // Special -const tkNewLine: int = 98; -const tkEndOfFile: int = 99; -const tkUnknown: int = 100; +const tkOwn: int = 98; +const tkNewLine: int = 99; +const tkEndOfFile: int = 100; +const tkUnknown: int = 101; // --------------------------------------------------------------------------- // Token struct diff --git a/tests/hir_test b/tests/hir_test index 114a8aa..942a0fe 100755 Binary files a/tests/hir_test and b/tests/hir_test differ diff --git a/tests/parser_test b/tests/parser_test index a55e33c..f4a2f9f 100755 Binary files a/tests/parser_test and b/tests/parser_test differ diff --git a/tests/sema_test b/tests/sema_test index 38da2ce..bc4c51e 100755 Binary files a/tests/sema_test and b/tests/sema_test differ