A read-only slice's diagnostics name (clone v) wherever clone copies the element type

This commit is contained in:
Joseph Ferano 2026-09-25 14:31:21 +07:00
parent bb4705b7e0
commit 409c5f5c0d
2 changed files with 20 additions and 17 deletions

View File

@ -2419,12 +2419,11 @@ and const_steps ro (ty : Types.t) n =
| _ -> ro
(* A copy of a read-only slice's elements that can be written, spelled so it
compiles. Only for elements that own nothing: an element holding a Vec or
a Map — directly or inside a struct — would copy only its header, and the
copy would share the original's block. *)
compiles: [clone], for exactly the element types clone copies. It refuses
elements that own storage — a copy would share their blocks — and
elements that hold a dyn, which its allocator storage cannot root. *)
let const_copy env (e : Types.t) =
if owning env e then None
else Some (Printf.sprintf "(slice (into v (vec-new %s)))" (Types.to_string e))
if owning env e || holds_dyn env e then None else Some "(clone v)"
(* A store through a read-only view: a [[const T]] or a (Ptr const T). *)
let refuse_const_place env loc (view : Types.t) =
@ -3348,19 +3347,12 @@ let numeric_note ~(want : Types.t) ~(got : Types.t) =
(%s x)"
(Types.to_string want)
(* The rest of the sentence when a read-only slice meets a writable one. Both
copies it names compile today: [string] reads any byte slice and [bytes]
copies a string, and [into] pushes any slice's elements into a Vec that
[slice] then views. *)
(* The rest of the sentence when a read-only slice meets a writable one. *)
let const_note env ~(want : Types.t) ~(got : Types.t) =
match want, got with
| Types.Slice (Types.Mut, e), Types.Slice (Types.Const, e')
when Types.equal e e' ->
let copy =
match e with
| Types.Int Types.U8 -> Some "(bytes (string v))"
| _ -> const_copy env e
in
let copy = const_copy env e in
Printf.sprintf
" — a %s can only be read, and never becomes a %s that can be written \
through. %sWhere nothing writes through it, the %s can be declared %s \
@ -3989,8 +3981,8 @@ and refuse_owned_copy ctx (r : Tast.expr) =
match r.Tast.ty with
| (Types.Vec _ | Types.Map _) when not (region_only ctx.env r.Tast.ty) ->
Printf.sprintf "(clone v) copies it into a %s of its own" t
(* TODO.org, "(clone slice)": once clone copies any value that owns
storage, this should say (clone v) too. *)
(* Nothing copies an array, an Option or a struct that owns storage,
nor a container whose elements do: its address is the way to it. *)
| _ -> Printf.sprintf "(addr v) gives a (Ptr const %s) to read it through" t
in
Loc.failk "check/const-owned-copy" r.Tast.loc

View File

@ -2283,7 +2283,18 @@ let () =
~needle:"expected [u8], found [const u8]";
rejects_check "and the refusal names the copy"
"(defn g [b [u8]] () (set (at b 0) 1)) (defn f [s [const u8]] () (g s))"
~needle:"(bytes (string v)) copies v";
~needle:"(clone v) copies v into a [u8] of its own";
accepts "and the copy it names compiles"
"(defn g [b [u8]] () (set (at b 0) 1)) (defn f [s [const u8]] () (g (clone s)))";
rejects_check "a store through a const slice names clone"
"(defstruct P [x i32]) (defn f [v [const P]] () (set (.x (at v 0)) 1))"
~needle:"(clone v) copies v's elements into one";
accepts "and that clone compiles"
"(defstruct P [x i32]) \
(defn f [v [const P]] () (let [w (clone v)] (set (.x (at w 0)) 1)))";
rejects_check "no clone named for elements clone refuses"
"(defn f [v [const dyn]] () (set (at v 0) 1))"
~needle:"take it as a [dyn] instead";
rejects_check "a generic writer does not take a const slice"
"(defn f [s [const i32]] () (sort s))"
~needle:"sort takes a slice it may write through";