diff --git a/lib/resolve.ml b/lib/resolve.ml index b389ca35..260333c0 100644 --- a/lib/resolve.ml +++ b/lib/resolve.ml @@ -80,6 +80,16 @@ let create_context () : context = (* Cmd Msg — linear side-effect obligation builtins (Stage 11) *) def "cmd_none"; def "cmd_perform"; + (* Option / Result value constructors — seeded as builtin constructors so + the honest stdlib (stdlib/string.affine `char_at`, stdlib/result.affine, + …) resolves without importing the legacy stdlib/prelude. `Option`/`Result` + are already builtin type constructors; this closes the matching value-side + gap. A user/prelude `type Option`/`type Result` decl cleanly shadows these + (Hashtbl.replace in Symbol.define). Issue #122. *) + let defc name = + let _ = Symbol.define ctx.symbols name SKConstructor Span.dummy Public in () + in + defc "Some"; defc "None"; defc "Ok"; defc "Err"; ctx (** Record a use-site reference for a symbol (Phase C: find-references). *) diff --git a/lib/typecheck.ml b/lib/typecheck.ml index feb90ad5..88648911 100644 --- a/lib/typecheck.ml +++ b/lib/typecheck.ml @@ -1225,6 +1225,45 @@ let register_builtins (ctx : context) : unit = lowers each to a JS intrinsic. char ::= TCon "Char". *) let ty_char = TCon "Char" in let opt t = TApp (TCon "Option", [t]) in + (* Option / Result value constructors — seeded as polymorphic builtin + schemes so the honest stdlib resolves without the legacy prelude + (mirrors the Resolve.create_context constructor seeding). Codegen + (codegen_deno.ml:111-114) already provides the Some/None/Ok/Err + runtime; this is purely the front-end type side. A user/prelude + `type Option`/`type Result` decl shadows these. Issue #122. *) + let res a b = TApp (TCon "Result", [a; b]) in + let fresh_named () = + let tv = fresh_tyvar 0 in + let v = (match tv with + | TVar r -> (match !r with Unbound (v, _) -> v | _ -> assert false) + | _ -> assert false) in + (v, tv) + in + let (v_none, t_none) = fresh_named () in + bind_scheme ctx "None" + { sc_tyvars = [(v_none, Types.KType)]; sc_effvars = []; sc_rowvars = []; + sc_body = opt t_none }; + Hashtbl.replace ctx.constructor_env "None" (opt t_none); + let (v_some, t_some) = fresh_named () in + let some_ty = TArrow (t_some, QOmega, opt t_some, EPure) in + bind_scheme ctx "Some" + { sc_tyvars = [(v_some, Types.KType)]; sc_effvars = []; sc_rowvars = []; + sc_body = some_ty }; + Hashtbl.replace ctx.constructor_env "Some" some_ty; + let (v_oka, t_oka) = fresh_named () in + let (v_okb, t_okb) = fresh_named () in + let ok_ty = TArrow (t_oka, QOmega, res t_oka t_okb, EPure) in + bind_scheme ctx "Ok" + { sc_tyvars = [(v_oka, Types.KType); (v_okb, Types.KType)]; + sc_effvars = []; sc_rowvars = []; sc_body = ok_ty }; + Hashtbl.replace ctx.constructor_env "Ok" ok_ty; + let (v_erra, t_erra) = fresh_named () in + let (v_errb, t_errb) = fresh_named () in + let err_ty = TArrow (t_errb, QOmega, res t_erra t_errb, EPure) in + bind_scheme ctx "Err" + { sc_tyvars = [(v_erra, Types.KType); (v_errb, Types.KType)]; + sc_effvars = []; sc_rowvars = []; sc_body = err_ty }; + Hashtbl.replace ctx.constructor_env "Err" err_ty; bind_var ctx "string_get" (TArrow (ty_string, QOmega, TArrow (ty_int, QOmega, ty_char, EPure), EPure)); bind_var ctx "string_sub" diff --git a/stdlib/string.affine b/stdlib/string.affine index 47d640ab..06976be6 100644 --- a/stdlib/string.affine +++ b/stdlib/string.affine @@ -106,8 +106,8 @@ fn substring(s: String, start: Int, end: Int) -> String { /// Repeat a string n times fn repeat(s: String, n: Int) -> String { - let result = ""; - let i = 0; + let mut result = ""; + let mut i = 0; while i < n { result = concat(result, s); i = i + 1; @@ -122,8 +122,8 @@ fn split(s: String, delimiter: String) -> [String] { if dlen == 0 { // Split into individual characters - let result = []; - let i = 0; + let mut result = []; + let mut i = 0; while i < slen { result = result ++ [string_sub(s, i, 1)]; i = i + 1; @@ -131,9 +131,9 @@ fn split(s: String, delimiter: String) -> [String] { return result; } - let result = []; - let current_start = 0; - let i = 0; + let mut result = []; + let mut current_start = 0; + let mut i = 0; while i <= slen - dlen { if string_sub(s, i, dlen) == delimiter { @@ -156,8 +156,8 @@ fn join(arr: [String], separator: String) -> String { return ""; } - let result = arr[0]; - let i = 1; + let mut result = arr[0]; + let mut i = 1; while i < len(arr) { result = concat(result, concat(separator, arr[i])); i = i + 1; @@ -173,8 +173,8 @@ fn replace(s: String, from: String, to_str: String) -> String { /// Reverse a string fn reverse_string(s: String) -> String { let slen = len(s); - let result = ""; - let i = slen - 1; + let mut result = ""; + let mut i = slen - 1; while i >= 0 { result = concat(result, string_sub(s, i, 1)); i = i - 1;