Skip to content

Commit

Permalink
sema: check duplicate fn arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
StunxFS committed Dec 27, 2024
1 parent 3160aa6 commit bfbc1b4
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 12 deletions.
2 changes: 1 addition & 1 deletion compiler/ast/Scope.v
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn (sc &Scope) derive() &Scope {

pub fn (mut sc Scope) add_symbol(sym Symbol) ! {
if _ := sc.lookup(sym.name) {
return error('duplicate variable `${sym.name}`')
return error('duplicate ${sym.type_of()} `${sym.name}`')
}
sc.syms << sym
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/ast/Stmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub:
return_type Type
pub mut:
stmts []Stmt
sym &Function = unsafe { nil }
scope &Scope = unsafe { nil }
}

pub struct FnArg {
Expand All @@ -41,6 +43,7 @@ pub:
name_pos FilePos
type Type
default_expr ?Expr
pos FilePos
}

pub struct WhileStmt {
Expand Down
23 changes: 22 additions & 1 deletion compiler/ast/Symbol.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,28 @@

module ast

pub type Symbol = Function | Constant | TypeSym
pub type Symbol = Function | Variable | Constant | TypeSym

pub fn (sym Symbol) type_of() string {
return match sym {
Function {
'function'
}
Variable {
if sym.is_arg {
'argument'
} else {
'variable'
}
}
Constant {
'constant'
}
TypeSym {
'type'
}
}
}

pub struct TypeSym {
pub:
Expand Down
14 changes: 12 additions & 2 deletions compiler/parser/stmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
mut args := []ast.FnArg{}
if !p.accept(.rparen) {
for {
mut arg_pos := p.tok.pos
arg_name := p.parse_ident()
arg_name_pos := p.prev_tok.pos
p.expect(.colon)
Expand All @@ -161,7 +162,8 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
if p.accept(.assign) {
arg_default_expr = p.parse_expr()
}
args << ast.FnArg{arg_name, arg_name_pos, arg_type, arg_default_expr}
arg_pos += p.prev_tok.pos
args << ast.FnArg{arg_name, arg_name_pos, arg_type, arg_default_expr, arg_pos}
if !p.accept(.comma) || p.should_abort() {
break
}
Expand All @@ -174,7 +176,15 @@ fn (mut p Parser) parse_fn_stmt(is_pub bool) ast.FnStmt {
p.ctx.void_type
}
stmts := p.parse_stmts()
return ast.FnStmt{p.tags, is_pub, name, name_pos, args, return_type, stmts}
return ast.FnStmt{
tags: p.tags
is_pub: is_pub
name: name
name_pos: name_pos
args: args
return_type: return_type
stmts: stmts
}
}

fn (mut p Parser) parse_let_stmt(is_pub bool) ast.LetStmt {
Expand Down
28 changes: 26 additions & 2 deletions compiler/sema/mod.v
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,37 @@ fn (mut sema Sema) stmt(mut stmt ast.Stmt) {
}

fn (mut sema Sema) fn_stmt(mut stmt ast.FnStmt) {
old_scope := sema.scope
defer {
sema.scope = old_scope
}

if sema.first_pass {
sema.scope.add_local_symbol(ast.Function{
stmt.sym = &ast.Function{
name: stmt.name
args: stmt.args
node: unsafe { stmt }
}) or { context.error(err.msg(), stmt.name_pos) }
}
stmt.scope = ast.Scope.new(sema.scope, ?ast.Symbol(stmt.sym))
sema.scope.add_local_symbol(stmt.sym) or { context.error(err.msg(), stmt.name_pos) }
sema.scope = stmt.scope
for arg in stmt.args {
sema.scope.add_symbol(ast.Variable{
name: arg.name
is_local: true
is_arg: true
type: arg.type
}) or {
context.error(err.msg(), arg.pos, context.Hint{
kind: .note
msg: 'inside function `${stmt.name}`'
})
}
}
sema.stmts(mut stmt.stmts)
return
}

sema.scope = stmt.scope
sema.stmts(mut stmt.stmts)
}
14 changes: 9 additions & 5 deletions tests/gen_out_files.vsh
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@
import os
import term

if !os.exists('rivetc') {
panic('rivetc executable not found')
const rivetc = './bin/rivetc'

if !os.exists(rivetc) {
panic('`${rivetc}` executable not found')
}

files := os.walk_ext('tests/', '.ri')
if files.len == 0 {
return
}

for file in files {
if !file.ends_with('.err.ri') {
out_file := file#[..-3] + '.out'
if !file.ends_with('.err.ri') || os.is_file(out_file) {
continue
}
println(term.bold('>> generating .out file for `${file}`'))
res := os.execute('./rivetc ${file}')
res := os.execute('${rivetc} ${file}')
if res.exit_code != 0 {
os.write_file(file#[..-3] + '.out', res.output.trim_space())!
os.write_file(out_file, res.output.trim_space())!
} else {
println(' >> unexpected .exit_code == 0 for `${file}`')
}
Expand Down
6 changes: 6 additions & 0 deletions tests/invalid/sema/duplicate_argument.err.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
tests/invalid/sema/duplicate_argument.err.ri:5:16: error: duplicate argument `a`
|
5 | fn win(a: int, a: int) {} // FAIL
| ^~~~~~
= note: inside function `win`
error: could not compile `duplicate_argument` module, aborting due to previous error
5 changes: 5 additions & 0 deletions tests/invalid/sema/duplicate_argument.err.ri
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {}

fn my_fn(a: int, b: int) {} // OK

fn win(a: int, a: int) {} // FAIL
2 changes: 1 addition & 1 deletion tests/run_tests.vsh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import term
// test.err.ri -> test.err.out
// test.ok.ri -> test.ok.out

const rivetc = 'bin/rivetc'
const rivetc = './bin/rivetc'

if !os.exists(rivetc) {
panic('`${rivetc}` executable not found')
Expand Down

0 comments on commit bfbc1b4

Please sign in to comment.