| ... | ... | @@ -442,6 +442,49 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block |
| 442 | 442 | try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements()); |
| 443 | 443 | } |
| 444 | 444 | |
| 445 | fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void { |
| 446 | // Look for the label in the scope. |
| 447 | var scope = parent_scope; |
| 448 | while (true) { |
| 449 | switch (scope.tag) { |
| 450 | .gen_zir => { |
| 451 | const gen_zir = scope.cast(Scope.GenZIR).?; |
| 452 | if (gen_zir.label) |prev_label| { |
| 453 | if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) { |
| 454 | const tree = parent_scope.tree(); |
| 455 | const label_src = tree.token_locs[label].start; |
| 456 | const prev_label_src = tree.token_locs[prev_label.token].start; |
| 457 | |
| 458 | const label_name = try mod.identifierTokenString(parent_scope, label); |
| 459 | const msg = msg: { |
| 460 | const msg = try mod.errMsg( |
| 461 | parent_scope, |
| 462 | label_src, |
| 463 | "redefinition of label '{s}'", |
| 464 | .{label_name}, |
| 465 | ); |
| 466 | errdefer msg.destroy(mod.gpa); |
| 467 | try mod.errNote( |
| 468 | parent_scope, |
| 469 | prev_label_src, |
| 470 | msg, |
| 471 | "previous definition is here", |
| 472 | .{}, |
| 473 | ); |
| 474 | break :msg msg; |
| 475 | }; |
| 476 | return mod.failWithOwnedErrorMsg(parent_scope, msg); |
| 477 | } |
| 478 | } |
| 479 | scope = gen_zir.parent; |
| 480 | }, |
| 481 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, |
| 482 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, |
| 483 | else => return, |
| 484 | } |
| 485 | } |
| 486 | } |
| 487 | |
| 445 | 488 | fn labeledBlockExpr( |
| 446 | 489 | mod: *Module, |
| 447 | 490 | parent_scope: *Scope, |
| ... | ... | @@ -457,6 +500,8 @@ fn labeledBlockExpr( |
| 457 | 500 | const tree = parent_scope.tree(); |
| 458 | 501 | const src = tree.token_locs[block_node.lbrace].start; |
| 459 | 502 | |
| 503 | try checkLabelRedefinition(mod, parent_scope, block_node.label); |
| 504 | |
| 460 | 505 | // Create the Block ZIR instruction so that we can put it into the GenZIR struct |
| 461 | 506 | // so that break statements can reference it. |
| 462 | 507 | const gen_zir = parent_scope.getGenZIR(); |
| ... | ... | @@ -560,14 +605,30 @@ fn varDecl( |
| 560 | 605 | .local_val => { |
| 561 | 606 | const local_val = s.cast(Scope.LocalVal).?; |
| 562 | 607 | if (mem.eql(u8, local_val.name, ident_name)) { |
| 563 | | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); |
| 608 | const msg = msg: { |
| 609 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ |
| 610 | ident_name, |
| 611 | }); |
| 612 | errdefer msg.destroy(mod.gpa); |
| 613 | try mod.errNote(scope, local_val.inst.src, msg, "previous definition is here", .{}); |
| 614 | break :msg msg; |
| 615 | }; |
| 616 | return mod.failWithOwnedErrorMsg(scope, msg); |
| 564 | 617 | } |
| 565 | 618 | s = local_val.parent; |
| 566 | 619 | }, |
| 567 | 620 | .local_ptr => { |
| 568 | 621 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 569 | 622 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 570 | | return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); |
| 623 | const msg = msg: { |
| 624 | const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{ |
| 625 | ident_name, |
| 626 | }); |
| 627 | errdefer msg.destroy(mod.gpa); |
| 628 | try mod.errNote(scope, local_ptr.ptr.src, msg, "previous definition is here", .{}); |
| 629 | break :msg msg; |
| 630 | }; |
| 631 | return mod.failWithOwnedErrorMsg(scope, msg); |
| 571 | 632 | } |
| 572 | 633 | s = local_ptr.parent; |
| 573 | 634 | }, |
| ... | ... | @@ -1166,8 +1227,10 @@ fn orelseCatchExpr( |
| 1166 | 1227 | return rlWrapPtr(mod, scope, rl, &block.base); |
| 1167 | 1228 | } |
| 1168 | 1229 | |
| 1169 | | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. |
| 1170 | | /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. |
| 1230 | /// Return whether the identifier names of two tokens are equal. Resolves @"" |
| 1231 | /// tokens without allocating. |
| 1232 | /// OK in theory it could do it without allocating. This implementation |
| 1233 | /// allocates when the @"" form is used. |
| 1171 | 1234 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { |
| 1172 | 1235 | const ident_name_1 = try mod.identifierTokenString(scope, token1); |
| 1173 | 1236 | const ident_name_2 = try mod.identifierTokenString(scope, token2); |
| ... | ... | @@ -1514,6 +1577,10 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1514 | 1577 | } |
| 1515 | 1578 | } |
| 1516 | 1579 | |
| 1580 | if (while_node.label) |label| { |
| 1581 | try checkLabelRedefinition(mod, scope, label); |
| 1582 | } |
| 1583 | |
| 1517 | 1584 | if (while_node.inline_token) |tok| |
| 1518 | 1585 | return mod.failTok(scope, tok, "TODO inline while", .{}); |
| 1519 | 1586 | |
| ... | ... | @@ -1649,7 +1716,16 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1649 | 1716 | return &while_block.base; |
| 1650 | 1717 | } |
| 1651 | 1718 | |
| 1652 | | fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst { |
| 1719 | fn forExpr( |
| 1720 | mod: *Module, |
| 1721 | scope: *Scope, |
| 1722 | rl: ResultLoc, |
| 1723 | for_node: *ast.Node.For, |
| 1724 | ) InnerError!*zir.Inst { |
| 1725 | if (for_node.label) |label| { |
| 1726 | try checkLabelRedefinition(mod, scope, label); |
| 1727 | } |
| 1728 | |
| 1653 | 1729 | if (for_node.inline_token) |tok| |
| 1654 | 1730 | return mod.failTok(scope, tok, "TODO inline for", .{}); |
| 1655 | 1731 | |
| ... | ... | @@ -1928,14 +2004,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1928 | 2004 | // Check for else/_ prong, those are handled last. |
| 1929 | 2005 | if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { |
| 1930 | 2006 | if (else_src) |src| { |
| 1931 | | const msg = try mod.errMsg( |
| 1932 | | scope, |
| 1933 | | case_src, |
| 1934 | | "multiple else prongs in switch expression", |
| 1935 | | .{}, |
| 1936 | | ); |
| 1937 | | errdefer msg.destroy(mod.gpa); |
| 1938 | | try mod.errNote(scope, src, msg, "previous else prong is here", .{}); |
| 2007 | const msg = msg: { |
| 2008 | const msg = try mod.errMsg( |
| 2009 | scope, |
| 2010 | case_src, |
| 2011 | "multiple else prongs in switch expression", |
| 2012 | .{}, |
| 2013 | ); |
| 2014 | errdefer msg.destroy(mod.gpa); |
| 2015 | try mod.errNote(scope, src, msg, "previous else prong is here", .{}); |
| 2016 | break :msg msg; |
| 2017 | }; |
| 1939 | 2018 | return mod.failWithOwnedErrorMsg(scope, msg); |
| 1940 | 2019 | } |
| 1941 | 2020 | else_src = case_src; |
| ... | ... | @@ -1945,14 +2024,17 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1945 | 2024 | mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) |
| 1946 | 2025 | { |
| 1947 | 2026 | if (underscore_src) |src| { |
| 1948 | | const msg = try mod.errMsg( |
| 1949 | | scope, |
| 1950 | | case_src, |
| 1951 | | "multiple '_' prongs in switch expression", |
| 1952 | | .{}, |
| 1953 | | ); |
| 1954 | | errdefer msg.destroy(mod.gpa); |
| 1955 | | try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); |
| 2027 | const msg = msg: { |
| 2028 | const msg = try mod.errMsg( |
| 2029 | scope, |
| 2030 | case_src, |
| 2031 | "multiple '_' prongs in switch expression", |
| 2032 | .{}, |
| 2033 | ); |
| 2034 | errdefer msg.destroy(mod.gpa); |
| 2035 | try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); |
| 2036 | break :msg msg; |
| 2037 | }; |
| 1956 | 2038 | return mod.failWithOwnedErrorMsg(scope, msg); |
| 1957 | 2039 | } |
| 1958 | 2040 | underscore_src = case_src; |
| ... | ... | @@ -1962,15 +2044,18 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1962 | 2044 | |
| 1963 | 2045 | if (else_src) |some_else| { |
| 1964 | 2046 | if (underscore_src) |some_underscore| { |
| 1965 | | const msg = try mod.errMsg( |
| 1966 | | scope, |
| 1967 | | switch_src, |
| 1968 | | "else and '_' prong in switch expression", |
| 1969 | | .{}, |
| 1970 | | ); |
| 1971 | | errdefer msg.destroy(mod.gpa); |
| 1972 | | try mod.errNote(scope, some_else, msg, "else prong is here", .{}); |
| 1973 | | try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); |
| 2047 | const msg = msg: { |
| 2048 | const msg = try mod.errMsg( |
| 2049 | scope, |
| 2050 | switch_src, |
| 2051 | "else and '_' prong in switch expression", |
| 2052 | .{}, |
| 2053 | ); |
| 2054 | errdefer msg.destroy(mod.gpa); |
| 2055 | try mod.errNote(scope, some_else, msg, "else prong is here", .{}); |
| 2056 | try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); |
| 2057 | break :msg msg; |
| 2058 | }; |
| 1974 | 2059 | return mod.failWithOwnedErrorMsg(scope, msg); |
| 1975 | 2060 | } |
| 1976 | 2061 | } |