@@ -27,6 +27,49 @@ pub enum Const {
2727 BigInt ( Rc < str > ) ,
2828}
2929
30+ /// A compilation's source text plus its line-start index, shared (one `Rc`)
31+ /// by every `FuncProto` the compilation produced. Exists so stack frames can
32+ /// resolve a bytecode position table entry (a byte offset — see
33+ /// [`FuncProto::pos`]) into a 1-based line/column *lazily, on the error path
34+ /// only*: the happy path pays one `Rc` per proto instead of an eager per-op
35+ /// line/column resolution at compile time.
36+ #[ derive( Debug ) ]
37+ pub struct SourceInfo {
38+ text : Rc < str > ,
39+ /// Byte offset of each line start in `text` (`[0]` is always 0).
40+ line_starts : Box < [ u32 ] > ,
41+ }
42+
43+ impl SourceInfo {
44+ pub fn new ( text : Rc < str > ) -> SourceInfo {
45+ let mut line_starts = vec ! [ 0u32 ] ;
46+ for ( i, b) in text. bytes ( ) . enumerate ( ) {
47+ if b == b'\n' {
48+ line_starts. push ( i as u32 + 1 ) ;
49+ }
50+ }
51+ SourceInfo {
52+ text,
53+ line_starts : line_starts. into_boxed_slice ( ) ,
54+ }
55+ }
56+
57+ /// 1-based (line, column) of byte `offset` in the source. Columns count
58+ /// characters, not bytes, matching the parser's diagnostics.
59+ pub fn line_col_of ( & self , offset : u32 ) -> ( u32 , u32 ) {
60+ let offset = offset. min ( self . text . len ( ) as u32 ) ;
61+ let line = self . line_starts . partition_point ( |& s| s <= offset) . max ( 1 ) ;
62+ let start = self . line_starts [ line - 1 ] as usize ;
63+ let col = self
64+ . text
65+ . get ( start..offset as usize )
66+ . map ( |s| s. chars ( ) . count ( ) )
67+ . unwrap_or ( 0 ) as u32
68+ + 1 ;
69+ ( line as u32 , col)
70+ }
71+ }
72+
3073/// How a free variable referenced by a nested function is captured at closure
3174/// creation time.
3275#[ derive( Clone , Copy , Debug ) ]
@@ -135,11 +178,24 @@ pub struct FuncProto {
135178 pub kind : FuncKind ,
136179 /// Source span for stack traces (start byte offset).
137180 pub source_start : u32 ,
138- /// 1-based line/column of the function's definition site in its source,
139- /// rendered in error stack traces (`at name (line:col)`). `0` = unknown
140- /// (synthetic protos, sources compiled without position tracking).
181+ /// 1-based line/column of the function's definition site in its source.
182+ /// `0` = unknown (synthetic protos, sources compiled without position
183+ /// tracking). Stack frames prefer the per-op position (`pos` at the
184+ /// frame's current ip); this is the fallback when no source is attached.
141185 pub source_line : u32 ,
142186 pub source_col : u32 ,
187+ /// Per-op source position table, index-parallel to `code`: the byte
188+ /// offset (into `source_info`'s text) of the statement or call the op was
189+ /// emitted for. Lets a stack frame report where the frame *is* — the
190+ /// throwing statement for the innermost frame, the call site for outer
191+ /// frames — instead of where its function was declared. Maintained by
192+ /// every code-shortening pass (fusion remaps it alongside the ops);
193+ /// resolved to line/column only on the error path.
194+ pub pos : Box < [ u32 ] > ,
195+ /// The compilation's shared source text + line index used to resolve
196+ /// `pos` entries. `None` for synthetic protos and sources compiled
197+ /// without text, which then fall back to the definition site.
198+ pub source_info : Option < Rc < SourceInfo > > ,
143199 /// Which source this function came from — the module key/path supplied to
144200 /// [`crate::compiler::compile_module_labeled`] — rendered in stack frames
145201 /// as `at name (label:line:col)` so an embedder can resolve the frame back
@@ -257,6 +313,12 @@ pub struct IcEntry {
257313}
258314
259315impl FuncProto {
316+ /// Source position (byte offset) recorded for the op at `ip`, or `None`
317+ /// when this proto carries no position table (synthetic protos).
318+ pub fn pos_at ( & self , ip : usize ) -> Option < u32 > {
319+ self . pos . get ( ip) . copied ( )
320+ }
321+
260322 pub fn empty ( name : & str , kind : FuncKind ) -> FuncProto {
261323 FuncProto {
262324 name : name. to_string ( ) ,
@@ -274,6 +336,8 @@ impl FuncProto {
274336 source_start : 0 ,
275337 source_line : 0 ,
276338 source_col : 0 ,
339+ pos : Box :: new ( [ ] ) ,
340+ source_info : None ,
277341 source_label : None ,
278342 uses_arguments : false ,
279343 param_names : Vec :: new ( ) ,
0 commit comments