Fix bug with float literal methods

This commit is contained in:
Richard Feldman 2025-11-23 23:51:09 -05:00
parent af8a40929c
commit bd48daaa21
No known key found for this signature in database
4 changed files with 15 additions and 102 deletions

View file

@ -719,8 +719,10 @@ pub fn getExpr(store: *const NodeStore, expr: CIR.Expr.Idx) CIR.Expr {
} };
},
.expr_dot_access => {
// Subtract 1 to undo the +1 offset used during storage
// This allows distinguishing empty args {start:0, len:0} from null args
const args_span = if (node.data_3 != 0) blk: {
const packed_span = FunctionArgs.fromU32(node.data_3);
const packed_span = FunctionArgs.fromU32(node.data_3 - 1);
const data_span = packed_span.toDataSpan();
break :blk CIR.Expr.Span{ .span = data_span };
} else null;
@ -1570,11 +1572,12 @@ pub fn addExpr(store: *NodeStore, expr: CIR.Expr, region: base.Region) Allocator
node.data_2 = @bitCast(e.field_name);
if (e.args) |args| {
// Use PackedDataSpan for efficient storage - FunctionArgs config is good for method call args
// Add 1 to distinguish empty args {start:0, len:0} (becomes 1) from null args (remains 0)
std.debug.assert(FunctionArgs.canFit(args.span));
const packed_span = FunctionArgs.fromDataSpanUnchecked(args.span);
node.data_3 = packed_span.toU32();
node.data_3 = packed_span.toU32() + 1;
} else {
node.data_3 = 0; // No args
node.data_3 = 0; // No args (null)
}
},
.e_runtime_error => |e| {

View file

@ -1894,7 +1894,7 @@ pub fn parseExprWithBp(self: *Parser, min_bp: u8) Error!AST.Expr.Idx {
.Int => {
self.advance();
// Disallow NoSpaceDotInt after Int (ambiguous with floats like 35.123)
// Disallow NoSpaceDotInt after Int (ambiguous with decimal literals like 35.123)
// But allow NoSpaceDotLowerIdent for method calls like 35.to_str()
if (self.peek() == .NoSpaceDotInt) {
return try self.pushMalformed(AST.Expr.Idx, .expr_dot_suffix_not_allowed, self.pos);

View file

@ -1,58 +1,13 @@
# META
~~~ini
description=Method call directly on float literal
type=snippet
type=repl
~~~
# SOURCE
~~~roc
age : Str
age = 12.34.to_str()
» 12.34.foo()
~~~
# EXPECTED
NIL
# OUTPUT
Crash: Num.Dec does not implement foo
# PROBLEMS
NIL
# TOKENS
~~~zig
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,Float,NoSpaceDotLowerIdent,NoSpaceOpenRound,CloseRound,
EndOfFile,
~~~
# PARSE
~~~clojure
(file
(type-module)
(statements
(s-type-anno (name "age")
(ty (name "Str")))
(s-decl
(p-ident (raw "age"))
(e-field-access
(e-frac (raw "12.34"))
(e-apply
(e-ident (raw "to_str")))))))
~~~
# FORMATTED
~~~roc
NO CHANGE
~~~
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "age"))
(e-dot-access (field "to_str")
(receiver
(e-dec-small (numerator "1234") (denominator-power-of-ten "2") (value "12.34")))
(args))
(annotation
(ty-lookup (name "Str") (builtin)))))
~~~
# TYPES
~~~clojure
(inferred-types
(defs
(patt (type "Str")))
(expressions
(expr (type "Str"))))
~~~

View file

@ -1,58 +1,13 @@
# META
~~~ini
description=Method call directly on integer literal
type=snippet
type=repl
~~~
# SOURCE
~~~roc
age : Str
age = 35.to_str()
» 35.foo()
~~~
# EXPECTED
NIL
# OUTPUT
Crash: Num.Dec does not implement foo
# PROBLEMS
NIL
# TOKENS
~~~zig
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,Int,NoSpaceDotLowerIdent,NoSpaceOpenRound,CloseRound,
EndOfFile,
~~~
# PARSE
~~~clojure
(file
(type-module)
(statements
(s-type-anno (name "age")
(ty (name "Str")))
(s-decl
(p-ident (raw "age"))
(e-field-access
(e-int (raw "35"))
(e-apply
(e-ident (raw "to_str")))))))
~~~
# FORMATTED
~~~roc
NO CHANGE
~~~
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "age"))
(e-dot-access (field "to_str")
(receiver
(e-num (value "35")))
(args))
(annotation
(ty-lookup (name "Str") (builtin)))))
~~~
# TYPES
~~~clojure
(inferred-types
(defs
(patt (type "Str")))
(expressions
(expr (type "Str"))))
~~~