authorchloe caruso <git@paperclover.net> 2024-05-25 22:45:31-07:00
committerchloe caruso <git@paperclover.net> 2024-05-25 22:45:31-07:00
log24269f1337e96920ebad63795ef0034c1eabbfb0
tree50c429271ba632c4d9a6a3bda649ed95e5b9c147
parent91ee439c7b9f751d6bceaa26282cfdd0b3cdc6ca

implement some more checks


13 files changed, 347 insertions(+), 371 deletions(-)

CHANGELOG.md+9-2
...@@ -1,7 +1,14 @@...@@ -1,7 +1,14 @@
1## 0.1.21## 0.1.2
22
3- feat: semantic analysis can return formatted errors to the user
4- feat: implement basic type coersions
5- feat: type-check call parameters
6- feat: type-check function return types
3- feat: parsing of assignments within expressions is disallowed now7- feat: parsing of assignments within expressions is disallowed now
4- feat: debug builds have `--report-trace` to print stack traces8- feat: limit parsing to 4096 nested expressions / modules
9- feat: added error for return outside of function
10- feat: debug builds have `--report-trace` to print a stack trace per error
11- fix: function arguments were not validated
5- fix: unused warnings now properly work12- fix: unused warnings now properly work
6- fix: empty files and functions no longer crash13- fix: empty files and functions no longer crash
714
...@@ -16,4 +23,4 @@...@@ -16,4 +23,4 @@
1623
17## 0.1.024## 0.1.0
1825
19initial demo of compiler pipeline in zig.26initial demo of compiler pipeline
README.md+5-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1# Compiler1# New Language Project
22
3This language is a mix of features from JavaScript, Rust, and Zig. Some notable features carried over from each include:3This language is a mix of features from JavaScript, Rust, and Zig. Some notable features carried over from each include:
44
...@@ -8,7 +8,7 @@ This language is a mix of features from JavaScript, Rust, and Zig. Some notable...@@ -8,7 +8,7 @@ This language is a mix of features from JavaScript, Rust, and Zig. Some notable
88
9## Standard Library9## Standard Library
1010
11Currently, there is no standard library. One will be added later.11Currently, there is no standard library. One will be introduced later.
1212
13## Hello World13## Hello World
1414
...@@ -77,3 +77,6 @@ var number = 0; // `number` is a `float`, but not explicit...@@ -77,3 +77,6 @@ var number = 0; // `number` is a `float`, but not explicit
77var number: int = 0; // `number` is `int`77var number: int = 0; // `number` is `int`
78var number: float = 0; // `number` is `float`78var number: float = 0; // `number` is `float`
79```79```
80
81## Functions
82
demo.ix deleted-1
...@@ -1 +0,0 @@
1let x: int = 1.5;
notes deleted-19
...@@ -1,19 +0,0 @@
10.1.2
2- teg: useless discard
3- teg: dangling identifier
4- teg: remove `let _ = ` error
5- sema: .as instruction
6- sema: binary op type resolution (float + num_literal -> float)
70.1.3
8- parse/teg/sema: implement the comptime keyword
9- implement all the binary operators
10- implement all the unary operators
11- implement string escapes
12- implement proper float printing
13- js/link: add parenthesis when needed
140.1.4
15- parse: impl module, struct define, struct init, defer, and optionals
16- teg: impl the above, except anonymous struct initializers
17- sema: implement some built-in functions on floats
18 - float.intTruncate() to perform lossy int conversion
19
src/compiler/Air.Dump.zig+1-1
...@@ -235,7 +235,7 @@ pub const State = struct {...@@ -235,7 +235,7 @@ pub const State = struct {
235 .func => |func| {235 .func => |func| {
236 try tty.setColor(w, .bright_black);236 try tty.setColor(w, .bright_black);
237 try w.writeAll("<");237 try w.writeAll("<");
238 try s.dumpValue(w, func.ty);238 try s.dumpValue(w, func.ty.value());
239 try tty.setColor(w, .bright_black);239 try tty.setColor(w, .bright_black);
240 try w.writeAll(", ");240 try w.writeAll(", ");
241 const decl_name = s.ip.declPtrConst(func.owner_decl).name.toSlice(s.ip);241 const decl_name = s.ip.declPtrConst(func.owner_decl).name.toSlice(s.ip);
src/compiler/Ast.zig-85
...@@ -520,91 +520,6 @@ pub fn nodeToken(ast: Ast, node: NodeIndex) TokenIndex {...@@ -520,91 +520,6 @@ pub fn nodeToken(ast: Ast, node: NodeIndex) TokenIndex {
520 return ast.nodes.items(.main_token)[node];520 return ast.nodes.items(.main_token)[node];
521}521}
522522
523// pub fn operandNode(ast: Ast, node: NodeIndex, operand: OperandId) NodeIndex {
524// if (operand == .main_node) return node;
525
526// switch (ast.nodeTag(node)) {
527// .root => unreachable,
528// .block_void => unreachable,
529// .block_implicit_return => unreachable,
530// .var_decl,
531// => return switch (operand) {
532// .type => ast.nodeData(node).var_decl.type.unwrap().?,
533// .initializer => ast.nodeData(node).var_decl.init,
534// else => unreachable,
535// },
536
537// .label,
538// .group,
539// .negate,
540// .bit_not,
541// .bool_not,
542// .@"comptime",
543// => return switch (operand) {
544// .base => ast.nodeData(node).un.expr,
545// else => unreachable, // Invalid unary operand
546// },
547
548// .add,
549// .sub,
550// .mul,
551// .div,
552// .rem,
553// .pow,
554// .@"and",
555// .@"or",
556// .bit_and,
557// .bit_or,
558// .bit_xor,
559// .shl,
560// .shr,
561// .eq,
562// .ne,
563// .lt,
564// .le,
565// .gt,
566// .ge,
567// .assign,
568// .add_assign,
569// .sub_assign,
570// .mul_assign,
571// .div_assign,
572// .rem_assign,
573// .pow_assign,
574// .and_assign,
575// .or_assign,
576// .bit_and_assign,
577// .bit_or_assign,
578// .bit_xor_assign,
579// .shl_assign,
580// .shr_assign,
581// => {
582// const bin = ast.nodeData(node).bin;
583// return switch (operand) {
584// .lhs => bin.lhs,
585// .rhs => bin.rhs,
586// else => unreachable, // Not a valid operand for binary node
587// };
588// },
589
590// .fn_decl,
591// .param_decl,
592// .id,
593// .number_int,
594// .number_float,
595// .string,
596// .multiline_string,
597// .call,
598// .builtin_call,
599// .@"if",
600// .field,
601// .index,
602// .@"return",
603// .@"break",
604// => |tag| std.debug.panic("TODO: operandNode .{s}", .{@tagName(tag)}),
605// }
606// }
607
608pub fn fullFnDecl(ast: Ast, node: NodeIndex) full.FnDecl {523pub fn fullFnDecl(ast: Ast, node: NodeIndex) full.FnDecl {
609 const tok_tags = ast.tokens.items(.tag);524 const tok_tags = ast.tokens.items(.tag);
610525
src/compiler/Comp.zig+2-2
...@@ -122,7 +122,7 @@ pub const File = struct {...@@ -122,7 +122,7 @@ pub const File = struct {
122 return loaded_source;122 return loaded_source;
123 }123 }
124124
125 const GetAstError = GetSourceError || Parse.Error;125 pub const GetAstError = GetSourceError || Parse.Error;
126126
127 pub fn getAst(file: *File, comp: *Comp) GetAstError!Ast {127 pub fn getAst(file: *File, comp: *Comp) GetAstError!Ast {
128 if (file.ast) |ast| {128 if (file.ast) |ast| {
...@@ -155,7 +155,7 @@ pub const File = struct {...@@ -155,7 +155,7 @@ pub const File = struct {
155 return ast;155 return ast;
156 }156 }
157157
158 const GetTegError = GetAstError || Transform.Error;158 pub const GetTegError = GetAstError || Transform.Error;
159159
160 pub fn getTeg(file: *File, comp: *Comp, fail_report: *Report) GetTegError!Teg {160 pub fn getTeg(file: *File, comp: *Comp, fail_report: *Report) GetTegError!Teg {
161 if (file.teg) |teg| {161 if (file.teg) |teg| {
src/compiler/InternPool.zig+66-8
...@@ -137,7 +137,7 @@ pub const Tag = enum(u8) {...@@ -137,7 +137,7 @@ pub const Tag = enum(u8) {
137 pub const FuncDecl = struct {137 pub const FuncDecl = struct {
138 analysis: FuncAnalysis,138 analysis: FuncAnalysis,
139 owner_decl: DeclIndex,139 owner_decl: DeclIndex,
140 ty: Value,140 ty: Type,
141 teg_body_inst: TrackedInst.Index,141 teg_body_inst: TrackedInst.Index,
142 };142 };
143143
...@@ -252,8 +252,7 @@ pub const Value = enum(u32) {...@@ -252,8 +252,7 @@ pub const Value = enum(u32) {
252 .int32 => .int32,252 .int32 => .int32,
253 .float => .float,253 .float => .float,
254 .string => .string,254 .string => .string,
255255 .func => value.toKey(ip).func.ty,
256 .func => @panic("TODO"),
257 };256 };
258 }257 }
259258
...@@ -274,6 +273,13 @@ pub const Value = enum(u32) {...@@ -274,6 +273,13 @@ pub const Value = enum(u32) {
274 };273 };
275 }274 }
276275
276 pub fn toTypeUnsafe(value: Value, proof_ip: *const InternPool) Type {
277 if (std.debug.runtime_safety) {
278 return value.toType(proof_ip).?;
279 }
280 return @enumFromInt(@intFromEnum(value));
281 }
282
277 pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {283 pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {
278 return val.valueData(ip, .string).get(ip);284 return val.valueData(ip, .string).get(ip);
279 }285 }
...@@ -298,6 +304,12 @@ pub const Type = enum(u32) {...@@ -298,6 +304,12 @@ pub const Type = enum(u32) {
298 void = @intFromEnum(Value.void),304 void = @intFromEnum(Value.void),
299 _,305 _,
300306
307 // Restricted version of `InternPool.Tag`
308 pub const Tag = enum(u32) {
309 type_function = @intFromEnum(InternPool.Tag.type_function),
310 simple_type = @intFromEnum(InternPool.Tag.simple_type),
311 };
312
301 /// An array of `Index` existing within the `extra` array.313 /// An array of `Index` existing within the `extra` array.
302 /// This type exists to provide a struct with lifetime that is314 /// This type exists to provide a struct with lifetime that is
303 /// not invalidated when items are added to the `InternPool`.315 /// not invalidated when items are added to the `InternPool`.
...@@ -314,9 +326,59 @@ pub const Type = enum(u32) {...@@ -314,9 +326,59 @@ pub const Type = enum(u32) {
314 return @enumFromInt(@intFromEnum(ty));326 return @enumFromInt(@intFromEnum(ty));
315 }327 }
316328
329 pub fn tag(ty: Type, ip: *const InternPool) Type.Tag {
330 return @enumFromInt(@intFromEnum(ty.value().valueTag(ip)));
331 }
332
333 pub fn toFunctionPrototype(ty: Type, ip: *const InternPool) ?Key.TypeFunction {
334 if (ty.tag(ip) != .type_function) return null;
335 return ip.indexToKey(ty.value()).type_function;
336 }
337
338 pub fn displayName(ty: Type, ip: *const InternPool) std.fmt.Formatter(formatDisplayName) {
339 return .{ .data = .{ .type = ty, .ip = ip } };
340 }
341
317 pub fn dump(ty: Type, ip: *const InternPool) std.fmt.Formatter(Value.formatValue) {342 pub fn dump(ty: Type, ip: *const InternPool) std.fmt.Formatter(Value.formatValue) {
318 return ty.value().dump(ip);343 return ty.value().dump(ip);
319 }344 }
345
346 fn formatDisplayName(
347 ctx: struct { type: Type, ip: *const InternPool },
348 comptime _: []const u8,
349 _: std.fmt.FormatOptions,
350 writer: anytype,
351 ) !void {
352 const ty = ctx.type;
353 const ip = ctx.ip;
354
355 switch (ty) {
356 .@"opaque" => return try writer.writeAll("opaque"),
357 .bool => return try writer.writeAll("bool"),
358 .int32 => return try writer.writeAll("int"),
359 .float => return try writer.writeAll("float"),
360 .string => return try writer.writeAll("string"),
361 .noreturn => return try writer.writeAll("noreturn"),
362 .type => return try writer.writeAll("type"),
363 .numeric_literal => return try writer.writeAll("numeric literal"),
364 .void => return try writer.writeAll("void"),
365 else => {},
366 }
367
368 switch (ty.tag(ip)) {
369 .simple_type => unreachable, // all simple types are already printed
370 .type_function => {
371 const type_function = ty.toFunctionPrototype(ip).?;
372 try writer.writeAll("fn(");
373 for (type_function.param_types, 0..) |param, i| {
374 if (i != 0) try writer.writeAll(", ");
375 try writer.print("{}", .{param.displayName(ip)});
376 }
377 try writer.writeAll(") -> ");
378 try writer.print("{}", .{type_function.return_type.displayName(ip)});
379 },
380 }
381 }
320};382};
321383
322pub const static_keys = [_]Key{384pub const static_keys = [_]Key{
...@@ -386,10 +448,6 @@ pub const String = enum(u32) {...@@ -386,10 +448,6 @@ pub const String = enum(u32) {
386 pub fn tryToNullTerminatedString(string: WithLen, ip: *InternPool, gpa: Allocator) !NullTerminatedString {448 pub fn tryToNullTerminatedString(string: WithLen, ip: *InternPool, gpa: Allocator) !NullTerminatedString {
387 return string.string.tryToNullTerminatedString(string.len, ip, gpa);449 return string.string.tryToNullTerminatedString(string.len, ip, gpa);
388 }450 }
389
390 pub fn toNullTerminatedString(string: WithLen, ip: *const InternPool) NullTerminatedString {
391 return string.string.tryToNullTerminatedString(string.len, ip);
392 }
393 };451 };
394452
395 pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 {453 pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 {
...@@ -678,7 +736,7 @@ pub const Key = union(enum) {...@@ -678,7 +736,7 @@ pub const Key = union(enum) {
678 };736 };
679737
680 pub const Func = struct {738 pub const Func = struct {
681 ty: Value,739 ty: Type,
682 owner_decl: DeclIndex,740 owner_decl: DeclIndex,
683 teg_body_inst: TrackedInst.Index,741 teg_body_inst: TrackedInst.Index,
684742
src/compiler/Parse.zig+40-1
...@@ -12,6 +12,11 @@ scratch: ArrayListUnmanaged(NodeIndex) = .{},...@@ -12,6 +12,11 @@ scratch: ArrayListUnmanaged(NodeIndex) = .{},
1212
13ast_error: ?AstError.WithTrace = null,13ast_error: ?AstError.WithTrace = null,
1414
15depth: u32 = 0,
16
17/// Instead of implementing a non-recursive parser, the stack size is limited by this constant.
18const depth_limit = 4096;
19
15pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, ast_error: *AstError.WithTrace) Parse.Error!Ast {20pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, ast_error: *AstError.WithTrace) Parse.Error!Ast {
16 var err_byte: ByteOffset = 0;21 var err_byte: ByteOffset = 0;
17 var tokens = Tokenizer.tokenize(22 var tokens = Tokenizer.tokenize(
...@@ -129,6 +134,8 @@ pub const AstError = union(enum) {...@@ -129,6 +134,8 @@ pub const AstError = union(enum) {
129 expected: ExpectedContext,134 expected: ExpectedContext,
130 got: Token,135 got: Token,
131 },136 },
137 /// Went past depth maximum
138 depth_limit: Token,
132139
133 pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report {140 pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report {
134 return (Report.Err{ .ast = err }).toReport(comp, file, trace);141 return (Report.Err{ .ast = err }).toReport(comp, file, trace);
...@@ -165,6 +172,9 @@ pub const AstError = union(enum) {...@@ -165,6 +172,9 @@ pub const AstError = union(enum) {
165 data.got.tag.description(),172 data.got.tag.description(),
166 });173 });
167 },174 },
175 .depth_limit => |data| {
176 try ctx.msg(.{ .token_stored = data }, "syntax tree is nested too deeply (limit: {d})", .{depth_limit});
177 },
168 }178 }
169 }179 }
170180
...@@ -200,6 +210,12 @@ pub fn parseRoot(p: *Parse) Error!void {...@@ -200,6 +210,12 @@ pub fn parseRoot(p: *Parse) Error!void {
200}210}
201211
202fn parseModuleMembers(p: *Parse) Error!SubList {212fn parseModuleMembers(p: *Parse) Error!SubList {
213 p.depth += 1;
214 defer p.depth -= 1;
215 if (p.depth > depth_limit) {
216 return p.fail(.{ .depth_limit = p.fullToken(p.pos) });
217 }
218
203 const trace = t.trace(@src());219 const trace = t.trace(@src());
204 defer trace.end();220 defer trace.end();
205221
...@@ -642,7 +658,24 @@ const Operator = enum(u8) {...@@ -642,7 +658,24 @@ const Operator = enum(u8) {
642 }658 }
643659
644 pub fn isAssign(op: Operator) bool {660 pub fn isAssign(op: Operator) bool {
645 return @intFromEnum(op) >= @intFromEnum(Operator.assign);661 return switch (op) {
662 .assign,
663 .add_assign,
664 .sub_assign,
665 .mul_assign,
666 .div_assign,
667 .rem_assign,
668 .pow_assign,
669 .and_assign,
670 .or_assign,
671 .bit_and_assign,
672 .bit_or_assign,
673 .bit_xor_assign,
674 .shl_assign,
675 .shr_assign,
676 => true,
677 else => false,
678 };
646 }679 }
647};680};
648681
...@@ -650,6 +683,12 @@ fn parseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex {...@@ -650,6 +683,12 @@ fn parseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex {
650 const trace = t.trace(@src());683 const trace = t.trace(@src());
651 defer trace.end();684 defer trace.end();
652685
686 p.depth += 1;
687 defer p.depth -= 1;
688 if (p.depth > depth_limit) {
689 return p.fail(.{ .depth_limit = p.fullToken(p.pos) });
690 }
691
653 assert(p.isExpressionStart());692 assert(p.isExpressionStart());
654693
655 // These unary operators are greedy, so handle them first.694 // These unary operators are greedy, so handle them first.
src/compiler/Report.zig+78-20
...@@ -64,16 +64,6 @@ pub const Err = union(enum) {...@@ -64,16 +64,6 @@ pub const Err = union(enum) {
64 );64 );
65 }65 }
66 },66 },
67 invalid_discard: struct {
68 node: NodeIndex,
69
70 fn build(data: @This(), ctx: *MsgContext) !void {
71 try ctx.msgToken(ctx.ast.idToken(data.node), "local cannot be named \"_\"", .{});
72 try ctx.msgAlone("to discard this value, remove '{s}'", .{
73 ctx.ast.tokenSlice(ctx.ast.nodeToken(data.node)),
74 });
75 }
76 },
77 duplicate_function: struct {67 duplicate_function: struct {
78 node: NodeIndex,68 node: NodeIndex,
79 prev: NodeIndex,69 prev: NodeIndex,
...@@ -106,7 +96,7 @@ pub const Err = union(enum) {...@@ -106,7 +96,7 @@ pub const Err = union(enum) {
106 fn build(data: @This(), ctx: *MsgContext) !void {96 fn build(data: @This(), ctx: *MsgContext) !void {
107 try ctx.msgNode(97 try ctx.msgNode(
108 data.node,98 data.node,
109 "return cannot be used here",99 "return cannot be used outside of a function body",
110 .{},100 .{},
111 );101 );
112 }102 }
...@@ -149,7 +139,34 @@ pub const Err = union(enum) {...@@ -149,7 +139,34 @@ pub const Err = union(enum) {
149139
150 fn build(data: @This(), ctx: *MsgContext) !void {140 fn build(data: @This(), ctx: *MsgContext) !void {
151 const node = try data.src.get(ctx);141 const node = try data.src.get(ctx);
152 try ctx.msgNode(node, "stinky", .{});142 try ctx.msgNode(node, "expected \"{s}\", got \"{s}\"", .{
143 data.to.displayName(&ctx.comp.intern_pool),
144 data.from.displayName(&ctx.comp.intern_pool),
145 });
146 }
147 },
148 call_non_function: struct {
149 call: LazyNode,
150 ty: Type,
151
152 fn build(data: @This(), ctx: *MsgContext) !void {
153 const node = try data.call.get(ctx);
154 try ctx.msgNode(node, "\"{s}\" is not callable", .{
155 data.ty.displayName(&ctx.comp.intern_pool),
156 });
157 }
158 },
159 call_mismatch_args: struct {
160 call: LazyNode,
161 actual: u32,
162 expected: u32,
163
164 fn build(data: @This(), ctx: *MsgContext) !void {
165 const node = try data.call.get(ctx);
166 try ctx.msgNode(node, "function expected {d} arguments, recieved {d}", .{
167 data.expected,
168 data.actual,
169 });
153 }170 }
154 },171 },
155};172};
...@@ -184,6 +201,20 @@ pub const Warn = union(enum) {...@@ -184,6 +201,20 @@ pub const Warn = union(enum) {
184 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});201 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});
185 }202 }
186 },203 },
204 invalid_discard: struct {
205 node: NodeIndex,
206
207 fn build(data: @This(), ctx: *MsgContext) !void {
208 try ctx.msgToken(ctx.ast.nodeToken(data.node), "\"let\" is not required for a discard", .{});
209 }
210 },
211 dangling_identifier: struct {
212 node: NodeIndex,
213
214 fn build(data: @This(), ctx: *MsgContext) !void {
215 try ctx.msgToken(ctx.ast.nodeToken(data.node), "dangling identifier does nothing", .{});
216 }
217 },
187 numeric_int_var_ambiguity: struct {218 numeric_int_var_ambiguity: struct {
188 node: LazyNode,219 node: LazyNode,
189220
...@@ -243,7 +274,7 @@ pub const DebugStoredTrace = if (builtin.mode == .Debug)...@@ -243,7 +274,7 @@ pub const DebugStoredTrace = if (builtin.mode == .Debug)
243 StoredTrace274 StoredTrace
244else275else
245 struct {276 struct {
246 pub fn capture() @This() {277 pub fn capture(_: ?usize) @This() {
247 return .{};278 return .{};
248 }279 }
249280
...@@ -289,7 +320,7 @@ pub fn deinit(report: Report, gpa: Allocator) void {...@@ -289,7 +320,7 @@ pub fn deinit(report: Report, gpa: Allocator) void {
289}320}
290321
291pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype, tty: std.io.tty.Config) !void {322pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype, tty: std.io.tty.Config) !void {
292 try writer.print("{d} errors, {d} warnings\n", .{ report.errors.len, report.warnings.len });323 // try writer.print("{d} errors, {d} warnings\n", .{ report.errors.len, report.warnings.len });
293 for (report.errors) |item| {324 for (report.errors) |item| {
294 try report.formatItem(item, .@"error", comp, writer, tty);325 try report.formatItem(item, .@"error", comp, writer, tty);
295 }326 }
...@@ -653,14 +684,16 @@ pub const LazyNode = struct {...@@ -653,14 +684,16 @@ pub const LazyNode = struct {
653684
654 const node_offset = teg.instNodeOffset(lazy.inst);685 const node_offset = teg.instNodeOffset(lazy.inst);
655686
656 const base_node: NodeIndex = @intCast(node_offset - teg_decl.src_node);687 const base_node: NodeIndex = @intCast(teg_decl.src_node + node_offset);
657 if (lazy.operand == .main_node)688 if (lazy.operand == .main_node)
658 return base_node;689 return base_node;
659690
660 const data = ctx.ast.nodeData(base_node);691 const data = ctx.ast.nodeData(base_node);
661692
693 const node_tag = ctx.ast.nodeTag(base_node);
694
662 switch (teg.instTag(lazy.inst)) {695 switch (teg.instTag(lazy.inst)) {
663 .as => switch (ctx.ast.nodeTag(base_node)) {696 .as => switch (node_tag) {
664 .var_decl => {697 .var_decl => {
665 switch (lazy.operand) {698 switch (lazy.operand) {
666 .rhs => return data.var_decl.init,699 .rhs => return data.var_decl.init,
...@@ -669,6 +702,23 @@ pub const LazyNode = struct {...@@ -669,6 +702,23 @@ pub const LazyNode = struct {
669 },702 },
670 else => {},703 else => {},
671 },704 },
705 .call => switch (node_tag) {
706 .call => switch (lazy.operand) {
707 .operand => return data.call.base,
708 else => |op| return ctx.ast.extraData(data.call.extra, Ast.Call)
709 .args.slice(ctx.ast)[op.getArgId()],
710 },
711 else => {},
712 },
713 .@"break" => switch (node_tag) {
714 .@"return" => return data.@"return".expr.unwrap().?,
715 .@"break" => return data.@"break".value.unwrap().?,
716 .block_implicit_return => {
717 const statements = ctx.ast.nodeStatements(base_node);
718 return statements[statements.len - 1];
719 },
720 else => return base_node,
721 },
672 else => {},722 else => {},
673 }723 }
674724
...@@ -687,14 +737,22 @@ pub const Operand = enum(u32) {...@@ -687,14 +737,22 @@ pub const Operand = enum(u32) {
687 main_node = 0,737 main_node = 0,
688 /// Base of a call, field, or operand to a unary instruction738 /// Base of a call, field, or operand to a unary instruction
689 operand = 1,739 operand = 1,
740 /// Return type of a function declaration or lambda node
741 return_type = 2,
690 /// Left hand side of a binary instruction742 /// Left hand side of a binary instruction
691 lhs,743 lhs = 4,
692 /// Right hand side of a binary instruction744 /// Right hand side of a binary instruction
693 rhs,745 rhs = 5,
694746
695 /// An argument of a call node747 /// One of:
748 /// - An argument of a .call node
749 /// - A parameter of a .fn_decl node
696 pub fn argument(id: u32) Operand {750 pub fn argument(id: u32) Operand {
697 return @enumFromInt(id + 2);751 return @enumFromInt(id + 5);
752 }
753
754 fn getArgId(op: Operand) u32 {
755 return @intFromEnum(op) - 5;
698 }756 }
699};757};
700758
src/compiler/Sema.zig+120-111
...@@ -23,14 +23,20 @@ report: Report.Builder = undefined,...@@ -23,14 +23,20 @@ report: Report.Builder = undefined,
23air_instructions: std.MultiArrayList(Air.Inst) = .{},23air_instructions: std.MultiArrayList(Air.Inst) = .{},
24air_extra: dod.ExtraStore(.air, .@"var") = .{},24air_extra: dod.ExtraStore(.air, .@"var") = .{},
2525
26temp_result_map: std.AutoArrayHashMapUnmanaged(Teg.Inst.Index, Type) = .{},
27
26pub const Error = error{28pub const Error = error{
27 /// Analysis has failed, see error list29 /// Analysis has failed, see error list
28 AnalysisFail,30 AnalysisFail,
29 /// This analysis is waiting on other dependencies to finish31 // This analysis is waiting on other dependencies to finish
30 Interrupt,32 // Interrupt,
33
34 // TODO: add handling of this
35 TooManyValues,
36 HasNulls,
3137
32 OutOfMemory,38 OutOfMemory,
33};39} || Comp.File.GetTegError;
3440
35/// Instead of using a hash map for this, just pre-allocate a list long enough to hold everything.41/// Instead of using a hash map for this, just pre-allocate a list long enough to hold everything.
36/// This is fine because all instructions are going to be adacent in a single declaration.42/// This is fine because all instructions are going to be adacent in a single declaration.
...@@ -104,6 +110,7 @@ pub fn deinit(s: *Sema) void {...@@ -104,6 +110,7 @@ pub fn deinit(s: *Sema) void {
104 s.scratch.deinit(s.arena);110 s.scratch.deinit(s.arena);
105 s.air_extra.deinit(s.gpa);111 s.air_extra.deinit(s.gpa);
106 s.air_instructions.deinit(s.gpa);112 s.air_instructions.deinit(s.gpa);
113 s.temp_result_map.deinit(s.arena);
107}114}
108115
109pub fn finalize(s: *Sema) !void {116pub fn finalize(s: *Sema) !void {
...@@ -121,7 +128,7 @@ pub fn finalize(s: *Sema) !void {...@@ -121,7 +128,7 @@ pub fn finalize(s: *Sema) !void {
121// --- analyze functions ---128// --- analyze functions ---
122129
123/// This is the entrypoint to semantic analysis: It all starts at a declaration.130/// This is the entrypoint to semantic analysis: It all starts at a declaration.
124pub fn analyzeDeclaration(s: *Sema) anyerror!Value {131pub fn analyzeDeclaration(s: *Sema) Error!Value {
125 const t = tracer.trace(@src());132 const t = tracer.trace(@src());
126 defer t.end();133 defer t.end();
127134
...@@ -137,27 +144,43 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {...@@ -137,27 +144,43 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
137 defer s.gpa.free(s.params);144 defer s.gpa.free(s.params);
138145
139 for (fn_decl.params.slice(&s.code), 0..) |param, i| {146 for (fn_decl.params.slice(&s.code), 0..) |param, i| {
140 s.params[i] = (try s.resolveRefComptime(param)).toType(s.ip).?;147 s.params[i] = try s.resolveType(
148 s.inst_map.start.op(Report.Operand.argument(@intCast(i))),
149 param,
150 );
141 }151 }
142152
143 const return_type = (try s.resolveRefComptime(fn_decl.return_type)).toType(s.ip).?;153 const return_type = try s.resolveType(
144154 s.inst_map.start.op(.return_type),
145 const root_block = fn_decl.body.toIndex() orelse155 fn_decl.return_type,
146 @panic("TODO: please change this to Index. Ref is not allowed here anymore");156 );
147157
148 _ = if (s.code.instPayload(.block, root_block).len > 0)158 const root_block = fn_decl.body;
149 try s.analyzeBlock(root_block)159 switch (root_block.decode()) {
150 else empty_block: {160 .value => |value| {
151 var root = try s.addBlock();161 const coerced = try s.coerceValue(
152 break :empty_block (try root.finish(s, s.air_instructions.len - 1));162 s.inst_map.start.op(.operand),
153 };163 value,
164 return_type,
165 );
166 const block = try s.addBlock();
167 _ = try s.addInstRef(.@"break", .{
168 .block_ref = block.block_index,
169 .value = coerced.toAir(),
170 });
171 },
172 .index => |index| {
173 try s.temp_result_map.putNoClobber(s.arena, index, return_type);
174 _ = try s.analyzeBlock(index);
175 },
176 }
154177
155 const func_prototype = try s.ip.get(s.gpa, .{178 const func_prototype = (try s.ip.get(s.gpa, .{
156 .type_function = .{179 .type_function = .{
157 .param_types = s.params,180 .param_types = s.params,
158 .return_type = return_type,181 .return_type = return_type,
159 },182 },
160 });183 })).toTypeUnsafe(s.ip);
161 const value = try s.ip.get(s.gpa, .{184 const value = try s.ip.get(s.gpa, .{
162 .func = .{185 .func = .{
163 .ty = func_prototype,186 .ty = func_prototype,
...@@ -169,7 +192,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {...@@ -169,7 +192,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
169 return value;192 return value;
170}193}
171194
172pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) anyerror!Air.Inst.Ref {195pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
173 const t = tracer.trace(@src());196 const t = tracer.trace(@src());
174 defer t.end();197 defer t.end();
175198
...@@ -224,7 +247,7 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -224,7 +247,7 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
224 return out;247 return out;
225}248}
226249
227fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {250fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
228 switch (s.code.instTag(in)) {251 switch (s.code.instTag(in)) {
229 .string => {252 .string => {
230 const string: Teg.SlicedString = s.code.instPayload(.string, in);253 const string: Teg.SlicedString = s.code.instPayload(.string, in);
...@@ -236,16 +259,14 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -236,16 +259,14 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
236 },259 },
237 .js => {260 .js => {
238 const operand_ref = s.code.instData(.js, in).operand;261 const operand_ref = s.code.instData(.js, in).operand;
239 const operand = try s.resolveRefComptime(operand_ref);262 const operand = try s.resolveRefComptimeType(in.op(.operand), operand_ref, .string);
240 // const operand_str = try s.coerceValue(operand_ref.toIndex().?, operand, .string);
241 return s.addInstRef(.js_global_ref, operand.asString(s.ip));263 return s.addInstRef(.js_global_ref, operand.asString(s.ip));
242 },264 },
243 .field => {265 .field => {
244 const field: Teg.Inst.Bin = s.code.instPayload(.field, in);266 const field: Teg.Inst.Bin = s.code.instPayload(.field, in);
245 const base = try s.resolveRefRuntime(field.lhs);267 const base = try s.resolveRefRuntime(field.lhs);
246 const rhs = try s.resolveRefComptime(field.rhs);
247268
248 // try s.validateType(rhs.type(s.ip), .string_type);269 const rhs = try s.resolveRefComptimeType(in.op(.rhs), field.rhs, .string);
249 const string = rhs.asString(s.ip);270 const string = rhs.asString(s.ip);
250271
251 const base_type = s.typeOfRef(base);272 const base_type = s.typeOfRef(base);
...@@ -331,66 +352,71 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -331,66 +352,71 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
331 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);352 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);
332 const operand = try s.resolveRefRuntime(brk.operand);353 const operand = try s.resolveRefRuntime(brk.operand);
333 const block_ref = s.inst_map.get(brk.block).?.decode().index; // block ref is resolved by teg354 const block_ref = s.inst_map.get(brk.block).?.decode().index; // block ref is resolved by teg
355
356 const coerced = if (s.temp_result_map.get(brk.block)) |ty|
357 try s.coerceRef(in.op(.operand), operand, ty)
358 else
359 operand;
360
334 return s.addInstRef(.@"break", .{361 return s.addInstRef(.@"break", .{
335 .block_ref = block_ref,362 .block_ref = block_ref,
336 .value = operand,363 .value = coerced,
337 });364 });
338 },365 },
339 .call => {366 .call => {
340 @panic("TODO: re-implement .call");367 const call: Teg.Inst.Call = s.code.instPayload(.call, in);
341 // const call: Teg.Inst.Call = s.code.instPayload(.call, in);368 const base = try s.resolveRefRuntime(call.base);
342 // const base = try s.resolveRefRuntime(call.base);369 const base_type = s.typeOfRef(base);
343 // const base_type = s.typeOfRef(base).toKey(s.ip);
344
345 // const is_opaque = base_type == .simple_type and base_type.simple_type == .@"opaque";
346
347 // const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
348 // defer s.arena.free(scratch);
349 // if (!is_opaque) {
350 // if (base_type != .type_function) {
351 // std.debug.print("Attempt to call non-function {}", .{base_type});
352 // return error.AnalysisFail;
353 // }
354
355 // const proto = base_type.type_function;
356 // if (proto.param_types.len != call.args.len) {
357 // std.debug.print("Incorrect args. Expect {d}, Got {d}", .{
358 // proto.param_types.len,
359 // call.args.len,
360 // });
361 // return error.AnalysisFail;
362 // }
363
364 // // validate function args
365 // for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
366 // const ref = try s.resolveRefRuntime(arg);
367 // scratch[i] = ref;
368 // _ = param;
369 // // try s.validateType(
370 // // Value.fromInterned(s.typeOfRef(ref)),
371 // // param,
372 // // );
373 // }
374 // } else {
375 // // just copy the args in without checking
376 // for (call.args.slice(&s.code), 0..) |arg, i| {
377 // const ref = try s.resolveRefRuntime(arg);
378 // scratch[i] = ref;
379 // }
380 // }
381370
382 // const start = s.air_extra.list.items.len;371 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
383 // try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch));372 defer s.arena.free(scratch);
373 if (base_type != .@"opaque") {
374 const proto = base_type.toFunctionPrototype(s.ip) orelse {
375 return s.fail(.call_non_function, .{
376 .call = s.nodeLoc(in, .operand),
377 .ty = base_type,
378 });
379 };
380
381 if (proto.param_types.len != call.args.len) {
382 // TODO: also attempt to coerce some of the params to reveal more errors.
383 return s.fail(.call_mismatch_args, .{
384 .call = s.nodeLoc(in, .operand),
385 .actual = @intCast(call.args.len),
386 .expected = @intCast(proto.param_types.len),
387 });
388 }
389
390 // validate function args
391 for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
392 const ref = try s.resolveRefRuntime(arg);
393 const coerced = try s.coerceRef(
394 in.op(Report.Operand.argument(@intCast(i))),
395 ref,
396 param,
397 );
398 scratch[i] = coerced;
399 }
400 } else {
401 // just copy the args in without checking
402 for (call.args.slice(&s.code), 0..) |arg, i| {
403 const ref = try s.resolveRefRuntime(arg);
404 scratch[i] = ref;
405 }
406 }
384407
385 // return s.addInstRef(.call, .{408 const start = s.air_extra.list.items.len;
386 // .base = base,409 try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch));
387 // .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{410
388 // .args = .{411 return s.addInstRef(.call, .{
389 // .start = @intCast(start),412 .base = base,
390 // .len = @intCast(scratch.len),413 .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{
391 // },414 .args = .{
392 // }),415 .start = @intCast(start),
393 // });416 .len = @intCast(scratch.len),
417 },
418 }),
419 });
394 },420 },
395 .ensure_void => {421 .ensure_void => {
396 const operand = try s.resolveRefRuntimeExtra(s.code.instData(.ensure_void, in).operand, false);422 const operand = try s.resolveRefRuntimeExtra(s.code.instData(.ensure_void, in).operand, false);
...@@ -488,7 +514,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {...@@ -488,7 +514,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
488 },514 },
489 .as => {515 .as => {
490 const as: Teg.Inst.As = s.code.instPayload(.as, in);516 const as: Teg.Inst.As = s.code.instPayload(.as, in);
491 const coerce_to = try s.resolveRefComptimeType(in.op(.lhs) ,as.type, .type);517 const coerce_to = try s.resolveRefComptimeType(in.op(.lhs), as.type, .type);
492 const operand = try s.resolveRefRuntime(as.operand);518 const operand = try s.resolveRefRuntime(as.operand);
493 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);519 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);
494 },520 },
...@@ -501,16 +527,14 @@ fn markInst(s: *Sema, inst: Air.Inst.Index) void {...@@ -501,16 +527,14 @@ fn markInst(s: *Sema, inst: Air.Inst.Index) void {
501 flags.uses = flags.uses.increment();527 flags.uses = flags.uses.increment();
502}528}
503529
504fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {530fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref {
505 const t = tracer.trace(@src());531 const t = tracer.trace(@src());
506 defer t.end();532 defer t.end();
507533
508 const scratch_top = s.scratch.items.len;534 const scratch_top = s.scratch.items.len;
509 defer s.scratch.shrinkRetainingCapacity(scratch_top);535 defer s.scratch.shrinkRetainingCapacity(scratch_top);
510536
511 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
512 const visited = try s.resolveRefRuntime(in);537 const visited = try s.resolveRefRuntime(in);
513 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
514538
515 if (scratch_top == s.scratch.items.len and visited.kind == .value) {539 if (scratch_top == s.scratch.items.len and visited.kind == .value) {
516 return visited;540 return visited;
...@@ -530,7 +554,7 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {...@@ -530,7 +554,7 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
530 return (try block.finish(s, scratch_top)).toRef();554 return (try block.finish(s, scratch_top)).toRef();
531}555}
532556
533fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.Inst.Ref {557fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) Error!Air.Inst.Ref {
534 const index = ref.toIndex() orelse {558 const index = ref.toIndex() orelse {
535 return ref.toValue().?.toAir();559 return ref.toValue().?.toAir();
536 };560 };
...@@ -548,11 +572,11 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air....@@ -548,11 +572,11 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.
548 return s.analyzeCode(index);572 return s.analyzeCode(index);
549}573}
550574
551fn resolveRefRuntime(s: *Sema, ref: Teg.Inst.Ref) anyerror!Air.Inst.Ref {575fn resolveRefRuntime(s: *Sema, ref: Teg.Inst.Ref) Error!Air.Inst.Ref {
552 return resolveRefRuntimeExtra(s, ref, true);576 return resolveRefRuntimeExtra(s, ref, true);
553}577}
554578
555fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {579fn resolveRefComptimeAny(s: *Sema, ref: Teg.Inst.Ref) !Value {
556 const index = switch (ref.decode()) {580 const index = switch (ref.decode()) {
557 .value => |value| return value,581 .value => |value| return value,
558 .index => |index| index,582 .index => |index| index,
...@@ -575,6 +599,15 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {...@@ -575,6 +599,15 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
575 }599 }
576}600}
577601
602fn resolveRefComptimeType(s: *Sema, src: Teg.InstOp, ref: Teg.Inst.Ref, ty: Type) !Value {
603 const any = try s.resolveRefComptimeAny(ref);
604 return try s.coerceValue(src, any, ty);
605}
606
607fn resolveType(s: *Sema, src: Teg.InstOp, ref: Teg.Inst.Ref) !Type {
608 return (try s.resolveRefComptimeType(src, ref, .type)).toTypeUnsafe(s.ip);
609}
610
578/// Coerce `value` runtime ref to `coerce_type`, and emit an error at `teg_ref`'s location611/// Coerce `value` runtime ref to `coerce_type`, and emit an error at `teg_ref`'s location
579fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref {612fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref {
580 const from = s.typeOfRef(value);613 const from = s.typeOfRef(value);
...@@ -628,30 +661,6 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {...@@ -628,30 +661,6 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
628 return value;661 return value;
629 }662 }
630 switch (to) {663 switch (to) {
631 // .simple_type => |simple_type| switch (simple_type) {
632 // // .@"opaque",
633 // .int32 => {
634 // switch (value_type.toKey(s.ip)) {
635 // .simple_type => switch (simple_type) {
636 // // .int32 => return value,
637 // // .numeric_literal => {
638 // // const val = value.decode().value.toKey(s.ip).comptime_float;
639 // // },
640 // else => |tag| std.debug.panic("TODO: coerceTo value_type simple_type .{s}", .{@tagName(tag)}),
641 // },
642 // else => |tag| std.debug.panic("TODO: coerceTo value_type .{s}", .{@tagName(tag)}),
643 // }
644 // },
645 // // .float,
646 // // .string,
647 // // .bool,
648 // // .noreturn,
649 // // .type,
650 // // .numeric_literal,
651 // else => |tag| std.debug.panic("TODO: coerceTo coerce_type .simple_type = .{s}", .{@tagName(tag)}),
652 // },
653 // else => |tag| std.debug.panic("TODO: coerceTo coerce_type .{s}", .{@tagName(tag)}),
654 // }
655 .@"opaque" => {664 .@"opaque" => {
656 //665 //
657 },666 },
...@@ -691,18 +700,18 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {...@@ -691,18 +700,18 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
691 //700 //
692 },701 },
693 .type => {702 .type => {
694 //703 if (value == .void) return .void;
695 },704 },
696 .numeric_literal => {},705 .numeric_literal => {},
697 .void => {706 .void => {
698 //707 if (value == .void) return .void;
699 },708 },
700 else => {},709 else => {},
701 }710 }
702 return s.coersionError(src, from, to);711 return s.coersionError(src, from, to);
703}712}
704713
705fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value {714fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {
706 const decl = s.ip.declPtr(decl_index);715 const decl = s.ip.declPtr(decl_index);
707 switch (decl.analysis) {716 switch (decl.analysis) {
708 .unreferenced => {717 .unreferenced => {
...@@ -945,7 +954,7 @@ fn coersionError(s: *Sema, src: InstOp, from: Type, to: Type) Error {...@@ -945,7 +954,7 @@ fn coersionError(s: *Sema, src: InstOp, from: Type, to: Type) Error {
945}954}
946955
947fn lazyNode(s: *const Sema, src: InstOp) Report.LazyNode {956fn lazyNode(s: *const Sema, src: InstOp) Report.LazyNode {
948 return src.lazyNode( s.decl_index);957 return src.lazyNode(s.decl_index);
949}958}
950959
951const Sema = @This();960const Sema = @This();
src/compiler/Transform.zig+22-8
...@@ -396,7 +396,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A...@@ -396,7 +396,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
396 });396 });
397397
398 t.getInstData(func).* = EncodedData.encode(.declaration, .{398 t.getInstData(func).* = EncodedData.encode(.declaration, .{
399 .src_node = 0,399 .src_node = outer_decl.nodeOffset(node),
400 .extra_index = decl_extra,400 .extra_index = decl_extra,
401 });401 });
402402
...@@ -971,8 +971,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc...@@ -971,8 +971,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
971 const ret_node = t.ast.nodeData(node).@"return";971 const ret_node = t.ast.nodeData(node).@"return";
972972
973 const ret_block_ptr = decl.return_target orelse {973 const ret_block_ptr = decl.return_target orelse {
974 // return error.InvalidReturn;974 try t.report.err(.invalid_return, .{ .node = node });
975 @panic("TODO");975 return .void;
976 };976 };
977977
978 const ret_block = if (ret_block_ptr.*) |block|978 const ret_block = if (ret_block_ptr.*) |block|
...@@ -1061,9 +1061,18 @@ fn transformBlockStatement(...@@ -1061,9 +1061,18 @@ fn transformBlockStatement(
1061 const var_decl = t.ast.fullVarDecl(node);1061 const var_decl = t.ast.fullVarDecl(node);
10621062
1063 if (mem.eql(u8, var_decl.id, "_")) {1063 if (mem.eql(u8, var_decl.id, "_")) {
1064 // TODO: relax this restriction into just creating a discard node1064 try t.report.warn(.invalid_discard, .{ .node = node });
1065 try t.report.err(.invalid_discard, .{ .node = node });1065
1066 return .void;1066 const ref = try t.transformExpression(decl, var_decl.init) orelse
1067 return null;
1068
1069 return try t.addInstRef(.{
1070 .tag = .discard,
1071 .data = EncodedData.encode(.discard, .{
1072 .src_node = decl.nodeOffset(node),
1073 .operand = ref,
1074 }),
1075 });
1067 }1076 }
10681077
1069 if (BuiltInIds.has(var_decl.id)) {1078 if (BuiltInIds.has(var_decl.id)) {
...@@ -1189,6 +1198,13 @@ fn transformBlockStatement(...@@ -1189,6 +1198,13 @@ fn transformBlockStatement(
1189 return null;1198 return null;
11901199
1191 if (!is_implicit_return_expression and result != .void) {1200 if (!is_implicit_return_expression and result != .void) {
1201 if (t.ast.nodeTag(node) == .id) {
1202 try t.report.warn(.dangling_identifier, .{
1203 .node = node,
1204 });
1205 return result;
1206 }
1207
1192 return try t.addInstRef(.{1208 return try t.addInstRef(.{
1193 .tag = .ensure_void,1209 .tag = .ensure_void,
1194 .data = EncodedData.encode(.ensure_void, .{1210 .data = EncodedData.encode(.ensure_void, .{
...@@ -1203,8 +1219,6 @@ fn transformBlockStatement(...@@ -1203,8 +1219,6 @@ fn transformBlockStatement(
1203 }1219 }
1204}1220}
12051221
1206// ---
1207
1208fn addRootInst(t: *Transform, inst: Inst) !Inst.Index {1222fn addRootInst(t: *Transform, inst: Inst) !Inst.Index {
1209 try t.insts.append(t.gpa, inst);1223 try t.insts.append(t.gpa, inst);
1210 return @enumFromInt(t.insts.len - 1);1224 return @enumFromInt(t.insts.len - 1);
src/compiler/main.zig+4-111
...@@ -131,7 +131,10 @@ pub fn main() !void {...@@ -131,7 +131,10 @@ pub fn main() !void {
131 try teg.dumpToWriter(stderr);131 try teg.dumpToWriter(stderr);
132 }132 }
133133
134 try comp.semaEntryPoint(main_file);134 comp.semaEntryPoint(main_file) catch |err| switch (err) {
135 error.AnalysisFail => std.process.exit(1),
136 else => return err,
137 };
135138
136 const output = std.io.getStdOut().writer();139 const output = std.io.getStdOut().writer();
137 var bufw = std.io.bufferedWriter(output);140 var bufw = std.io.bufferedWriter(output);
...@@ -140,114 +143,4 @@ pub fn main() !void {...@@ -140,114 +143,4 @@ pub fn main() !void {
140 try bufw.flush();143 try bufw.flush();
141144
142 std.process.cleanExit();145 std.process.cleanExit();
143
144 // var error_pos: u32 = 0;
145 // var tokens = Tokenizer.tokenize(source, alloc.allocator(), &error_pos) catch |err| {
146 // var line: usize = 1;
147 // var byte: usize = 0;
148 // var line_data: []const u8 = undefined;
149 // while (true) {
150 // const b = std.mem.indexOfScalarPos(u8, source, byte, '\n') orelse {
151 // line_data = source[byte..];
152 // break;
153 // };
154 // if (b > error_pos) {
155 // line_data = source[byte..b];
156 // break;
157 // }
158 // byte = b + 1;
159 // line += 1;
160 // }
161 // var col: usize = 1;
162 // var arrow_pos: usize = 0;
163 // while (byte < error_pos) {
164 // const b = source[byte];
165 // std.debug.assert(b != '\n');
166 // byte += std.unicode.utf8ByteSequenceLength(b) catch break;
167 // col += 1;
168
169 // arrow_pos += 1; // TODO: grapheme cluster
170 // }
171
172 // const stderr = std.io.getStdErr();
173 // const config = std.io.tty.detectConfig(stderr);
174 // var buf = std.io.bufferedWriter(stderr.writer());
175 // const writer = buf.writer();
176
177 // try config.setColor(writer, .red);
178 // try writer.writeAll("error");
179 // try config.setColor(writer, .bright_black);
180 // try writer.writeAll(": ");
181 // try config.setColor(writer, .reset);
182 // switch (err) {
183 // error.OutOfMemory => try writer.writeAll("Out of Memory"),
184 // error.InvalidUtf8 => try writer.writeAll("Invalid UTF-8"),
185 // error.InvalidSyntax => try writer.writeAll("Syntax Error"),
186 // error.TrailingNumericSeparator => try writer.writeAll("Trailing Numeric Separator"),
187 // error.UnterminatedString => try writer.writeAll("Unterminated String"),
188 // error.InvalidEscapeSequence => try writer.writeAll("Invalid Escape Sequence"),
189 // error.InvalidEscapeCodepoint => try writer.writeAll("Invalid Escape Codepoint"),
190 // error.GitConflict => try writer.writeAll("Found Git Conflict"),
191 // }
192 // try writer.print(" at {d}:{d}\n ", .{ line, col });
193 // try writer.writeAll(line_data);
194 // try writer.writeAll("\n ");
195 // try writer.writeByteNTimes(' ', col);
196
197 // try config.setColor(writer, .bright_green);
198 // try writer.writeAll("^");
199 // try config.setColor(writer, .reset);
200 // try writer.writeAll("\n");
201
202 // try buf.flush();
203 // return err;
204 // };
205 // defer tokens.deinit(alloc.allocator());
206
207 // // std.debug.print("tokens.len = {d}\n", .{tokens.len});
208 // // std.debug.print("token list bytes = {d}\n", .{tokens.toMultiArrayList().allocatedBytes().len});
209 // // std.debug.print("source bytes = {d}\n", .{source.len});
210 // // for (
211 // // tokens.items(.tag),
212 // // tokens.items(.offset),
213 // // ) |tag, offset| {
214 // // std.debug.print(".tag = {s} .offset = {d}\n", .{
215 // // @tagName(tag),
216 // // offset,
217 // // });
218 // // }
219
220 // const ast = ast: {
221 // var p = Parse.init(alloc.allocator(), source, tokens);
222 // errdefer p.deinit();
223 // try p.nodes.ensureUnusedCapacity(alloc.allocator(), 100);
224 // p.parseRoot() catch |err| {
225 // const tok = p.fullToken(p.pos);
226
227 // std.debug.print("ast_error = {?}\n", .{p.ast_error});
228
229 // std.debug.print("tok: {s} - '{s}' - {d}\n", .{
230 // @tagName(tok.tag),
231 // tok.slice(p.source),
232 // tok.offset,
233 // });
234
235 // return err;
236 // };
237 // break :ast try p.finalize(tokens);
238 // };
239
240 // try ast.dumpToWriter(std.io.getStdOut().writer());
241
242 // const teg = try Transform.transform(ast, alloc.allocator());
243 // std.debug.print("teg.insts.len = {d}\n", .{teg.insts.len});
244 // std.debug.print("teg.string_bytes.len = {d}\n", .{teg.string_bytes.len});
245 // std.debug.print("teg.extra.len = {d}\n", .{teg.extra.len});
246
247 // for (teg.insts.items(.tag), teg.insts.items(.data)) |tag, data| {
248 // std.debug.print("{s} - {}\n", .{
249 // @tagName(tag),
250 // data,
251 // });
252 // }
253}146}