1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker: apply stricter type checks to function args and return types

This commit is contained in:
Uwe Krüger 2020-06-01 21:15:59 +02:00 committed by GitHub
parent a7c84834f4
commit 076089d3c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 141 additions and 107 deletions

View File

@ -84,7 +84,7 @@ pub fn (instance BitField) get_bit(bitnr int) int {
if bitnr >= instance.size {
return 0
}
return (instance.field[bitslot(bitnr)] >> (bitnr % slot_size)) & u32(1)
return int((instance.field[bitslot(bitnr)] >> (bitnr % slot_size)) & u32(1))
}
// set_bit sets bit number 'bit_nr' to 1 (count from 0).

View File

@ -19,7 +19,7 @@ fn test_bf_set_clear_toggle_get() {
}
fn test_bf_and_not_or_xor() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input1 := bitfield.new(len)
mut input2 := bitfield.new(len)
@ -46,7 +46,7 @@ fn test_bf_and_not_or_xor() {
}
fn test_clone_cmp() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
for i in 0..len {
@ -60,7 +60,7 @@ fn test_clone_cmp() {
}
fn test_slice_join() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
for i in 0..len {
@ -83,7 +83,7 @@ fn test_slice_join() {
}
fn test_pop_count() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut count0 := 0
mut input := bitfield.new(len)
@ -98,7 +98,7 @@ fn test_pop_count() {
}
fn test_hamming() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut count := 0
mut input1 := bitfield.new(len)
@ -138,7 +138,7 @@ fn test_bf_from_bytes() {
}
fn test_bf_from_str() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := ''
for _ in 0..len {
@ -160,7 +160,7 @@ fn test_bf_from_str() {
}
fn test_bf_bf2str() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
for i in 0..len {
@ -188,7 +188,7 @@ fn test_bf_bf2str() {
}
fn test_bf_set_all() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
input.set_all()
@ -202,7 +202,7 @@ fn test_bf_set_all() {
}
fn test_bf_clear_all() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
for i in 0..len {
@ -221,7 +221,7 @@ fn test_bf_clear_all() {
}
fn test_bf_reverse() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
for i in 0..len {
@ -241,7 +241,7 @@ fn test_bf_reverse() {
}
fn test_bf_resize() {
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(rand.next(len) + 1)
for _ in 0..100 {
@ -259,7 +259,7 @@ fn test_bf_pos() {
* all haystacks here contain exactly one instanse of needle,
* so search should return non-negative-values
**/
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut result := 1
for i := 1; i < len; i++ { // needle size
@ -322,7 +322,7 @@ fn test_bf_rotate() {
}
fn test_bf_printing(){
rand.seed(time.now().unix)
rand.seed(int(time.now().unix))
len := 80
mut input := bitfield.new(len)
for i in 0..len {

View File

@ -111,7 +111,7 @@ fn new_dense_array(value_bytes int) DenseArray {
cap: 8
size: 0
deletes: 0
keys: &string(malloc(8 * sizeof(string)))
keys: &string(malloc(int(8 * sizeof(string))))
values: malloc(8 * value_bytes)
}
}
@ -198,7 +198,7 @@ fn new_map_1(value_bytes int) map {
cached_hashbits: max_cached_hashbits
shift: init_log_capicity
key_values: new_dense_array(value_bytes)
metas: &u32(vcalloc(sizeof(u32) * (init_capicity + extra_metas_inc)))
metas: &u32(vcalloc(int(sizeof(u32) * (init_capicity + extra_metas_inc))))
extra_metas: extra_metas_inc
size: 0
}
@ -323,7 +323,7 @@ fn (mut m map) rehash() {
fn (mut m map) cached_rehash(old_cap u32) {
old_metas := m.metas
m.metas = &u32(vcalloc(sizeof(u32) * (m.cap + 2 + m.extra_metas)))
m.metas = &u32(vcalloc(int(sizeof(u32) * (m.cap + 2 + m.extra_metas))))
old_extra_metas := m.extra_metas
for i := u32(0); i <= old_cap + old_extra_metas; i += 2 {
if old_metas[i] == 0 {
@ -430,8 +430,8 @@ pub fn (d DenseArray) clone() DenseArray {
cap: d.cap
size: d.size
deletes: d.deletes
keys: &string(malloc(d.cap * sizeof(string)))
values: byteptr(malloc(d.cap * u32(d.value_bytes)))
keys: &string(malloc(int(d.cap * sizeof(string))))
values: byteptr(malloc(int(d.cap * u32(d.value_bytes))))
}
C.memcpy(res.keys, d.keys, d.cap * sizeof(string))
C.memcpy(res.values, d.values, d.cap * u32(d.value_bytes))
@ -447,7 +447,7 @@ pub fn (m map) clone() map {
cached_hashbits: m.cached_hashbits
shift: m.shift
key_values: m.key_values.clone()
metas: &u32(malloc(metas_size))
metas: &u32(malloc(int(metas_size)))
extra_metas: m.extra_metas
size: m.size
}

View File

@ -121,13 +121,13 @@ fn (mut n mapnode) split_child(child_index int, y mut mapnode) {
z.values[j] = y.values[j + degree]
}
if !isnil(y.children) {
z.children = &voidptr(malloc(children_bytes))
z.children = &voidptr(malloc(int(children_bytes)))
for jj := degree - 1; jj >= 0; jj-- {
z.children[jj] = y.children[jj + degree]
}
}
if isnil(n.children) {
n.children = &voidptr(malloc(children_bytes))
n.children = &voidptr(malloc(int(children_bytes)))
}
n.children[n.size + 1] = n.children[n.size]
for j := n.size; j > child_index; j-- {

View File

@ -982,7 +982,7 @@ pub fn (s string) ustring() ustring {
// runes will have at least s.len elements, save reallocations
// TODO use VLA for small strings?
runes: __new_array(0, s.len, sizeof(int))
runes: __new_array(0, s.len, int(sizeof(int)))
}
for i := 0; i < s.len; i++ {
char_len := utf8_char_len(s.str[i])
@ -1000,7 +1000,7 @@ __global g_ustring_runes []int
pub fn (s string) ustring_tmp() ustring {
if g_ustring_runes.len == 0 {
g_ustring_runes = __new_array(0, 128, sizeof(int))
g_ustring_runes = __new_array(0, 128, int(sizeof(int)))
}
mut res := ustring{
s: s
@ -1048,7 +1048,7 @@ fn (u ustring) ge(a ustring) bool {
pub fn (u ustring) add(a ustring) ustring {
mut res := ustring{
s: u.s + a.s
runes: __new_array(0, u.s.len + a.s.len, sizeof(int))
runes: __new_array(0, u.s.len + a.s.len, int(sizeof(int)))
}
mut j := 0
for i := 0; i < u.s.len; i++ {

View File

@ -227,7 +227,7 @@ fn utf8_str_visible_length(s string) int {
// Reads an utf8 character from standard input
pub fn utf8_getchar() int {
c := C.getchar()
len := utf8_len(~c)
len := utf8_len(byte(~c))
if c < 0 {
return 0
}

View File

@ -183,7 +183,7 @@ pub fn new_context(cfg gg.Cfg) &FreeType {
C.glBlendFunc(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA)
shader := gl.new_shader('text')
shader.use()
projection := glm.ortho(0, width, 0, height) // 0 at BOT
projection := glm.ortho(0, f32(width), 0, f32(height)) // 0 at BOT
shader.set_mat4('projection', projection)
// FREETYPE
ft := C.FT_Library{}

View File

@ -100,7 +100,7 @@ pub fn new_context(cfg Cfg) &GG {
shader := gl.new_shader('simple')
shader.use()
if cfg.use_ortho {
projection := glm.ortho(0, cfg.width, cfg.height, 0)
projection := glm.ortho(0, f32(cfg.width), f32(cfg.height), 0)
shader.set_mat4('projection', projection)
}
else {
@ -304,7 +304,7 @@ fn todo_remove_me(cfg Cfg, scale int) {
//# glBlendFunc(C.GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader := gl.new_shader('text')
shader.use()
projection := glm.ortho(0, width, 0, height)// 0 at BOT
projection := glm.ortho(0, f32(width), 0, f32(height))// 0 at BOT
// projection_new := ortho(0, width, 0, height)// 0 at BOT
// projection := gl.ortho(0, width,height,0) // 0 at TOP
shader.set_mat4('projection', projection)

View File

@ -398,7 +398,7 @@ fn ortho_js(left, right, bottom, top f32) &f32 {
bt := 1.0 / (bottom - top)
nf := f32(1.0) / 1.0// (mynear -myfar)
mut out := &f32(0)
unsafe { out = &f32( malloc (sizeof(f32) * 16)) }
unsafe { out = &f32( malloc (int(sizeof(f32) * 16))) }
out[0] = -2.0 * lr
out[1] = 0
out[2] = 0

View File

@ -58,6 +58,6 @@ pub fn new(poly int) &Crc32 {
// calculate crc32 using ieee
pub fn sum(b []byte) u32 {
c := new(ieee)
c := new(int(ieee))
return c.sum32(b)
}

View File

@ -7,7 +7,7 @@ fn test_hash_crc32() {
assert sum1.hex() == '483f8cf0'
c := crc32.new(crc32.ieee)
c := crc32.new(int(crc32.ieee))
b2 := 'testing crc32 again'.bytes()
sum2 := c.checksum(b2)
assert sum2 == u32(1420327025)

View File

@ -208,7 +208,7 @@ pub fn reverse_32(x u32) u32 {
mut y := ((x>>u32(1) & (m0 & max_u32)) | ((x & (m0 & max_u32))<<1))
y = ((y>>u32(2) & (m1 & max_u32)) | ((y & (m1 & max_u32))<<u32(2)))
y = ((y>>u32(4) & (m2 & max_u32)) | ((y & (m2 & max_u32))<<u32(4)))
return reverse_bytes_32(y)
return reverse_bytes_32(u32(y))
}
// reverse_64 returns the value of x with its bits in reversed order.
@ -236,7 +236,7 @@ pub fn reverse_bytes_16(x u16) u16 {
[inline]
pub fn reverse_bytes_32(x u32) u32 {
y := ((x>>u32(8) & (m3 & max_u32)) | ((x & (m3 & max_u32))<<u32(8)))
return (y>>16) | (y<<16)
return u32((y>>16) | (y<<16))
}
// reverse_bytes_64 returns the value of x with its bytes in reversed order.

View File

@ -240,7 +240,7 @@ fn cmp_f64s(a, b f64) int {
// Two integers are safe to multiply when their bit lengths
// sum up to less than 64 (conservative estimate).
fn safe_to_multiply(a, b i64) bool {
return (bits.len_64(abs(a)) + bits.len_64(abs(b))) < 64
return (bits.len_64(u64(abs(a))) + bits.len_64(u64(abs(b)))) < 64
}
fn cmp(f1, f2 Fraction) int {

View File

@ -418,7 +418,7 @@ pub fn (mut ws Client) read() int {
} else if frame.opcode in [.text_frame, .binary_frame] {
data_node:
l.d('read: recieved text_frame or binary_frame')
mut payload := malloc(sizeof(byte) * u32(payload_len) + 1)
mut payload := malloc(int(sizeof(byte) * u32(payload_len) + 1))
if payload == 0 {
l.f('out of memory')
}
@ -438,7 +438,7 @@ pub fn (mut ws Client) read() int {
size += f.len
}
}
mut pl := malloc(sizeof(byte) * u32(size))
mut pl := malloc(int(sizeof(byte) * u32(size)))
if pl == 0 {
l.f('out of memory')
}

View File

@ -203,8 +203,8 @@ match c {
`B` { return .history_next }
`A` { return .history_previous }
`1` { return r.analyse_extended_control() }
`2` { return r.analyse_extended_control_no_eat(sequence) }
`3` { return r.analyse_extended_control_no_eat(sequence) }
`2` { return r.analyse_extended_control_no_eat(byte(sequence)) }
`3` { return r.analyse_extended_control_no_eat(byte(sequence)) }
else {}
}
}

View File

@ -15,7 +15,7 @@ const (
fn test_now_format() {
t := time.now()
u := t.unix
assert t.format() == time.unix(u).format()
assert t.format() == time.unix(int(u)).format()
}
fn test_format() {

View File

@ -8,5 +8,5 @@ const (
)
// random returns a random time struct in *the past*.
pub fn random() time.Time {
return time.unix(rand.next(start_time_unix))
return time.unix(rand.next(int(start_time_unix)))
}

View File

@ -118,7 +118,7 @@ pub fn new_time(t Time) Time {
// unix_time returns Unix time.
pub fn (t Time) unix_time() int {
if t.unix != 0 {
return t.unix
return int(t.unix)
}
tt := C.tm{
tm_sec: t.second
@ -134,12 +134,12 @@ pub fn (t Time) unix_time() int {
// add_seconds returns a new time struct with an added number of seconds.
pub fn (t Time) add_seconds(seconds int) Time {
// TODO Add(d time.Duration)
return unix(t.unix + u64(seconds))
return unix(int(t.unix + u64(seconds)))
}
// add_days returns a new time struct with an added number of days.
pub fn (t Time) add_days(days int) Time {
return unix(t.unix + u64(i64(days) * 3600 * 24))
return unix(int(t.unix + u64(i64(days) * 3600 * 24)))
}
// since returns a number of seconds elapsed since a given time.

View File

@ -6,7 +6,7 @@ module checker
import v.table
import v.token
pub fn (c &Checker) check_types(got, expected table.Type) bool {
pub fn (c &Checker) check_basic(got, expected table.Type) bool {
t := c.table
got_idx := t.unalias_num_type(got).idx()
exp_idx := t.unalias_num_type(expected).idx()
@ -114,10 +114,10 @@ pub fn (c &Checker) check_types(got, expected table.Type) bool {
exp_fn := exp_info.func
// we are using check() to compare return type & args as they might include
// functions themselves. TODO: optimize, only use check() when needed
if got_fn.args.len == exp_fn.args.len && c.check_types(got_fn.return_type, exp_fn.return_type) {
if got_fn.args.len == exp_fn.args.len && c.check_basic(got_fn.return_type, exp_fn.return_type) {
for i, got_arg in got_fn.args {
exp_arg := exp_fn.args[i]
if !c.check_types(got_arg.typ, exp_arg.typ) {
if !c.check_basic(got_arg.typ, exp_arg.typ) {
return false
}
}
@ -157,55 +157,57 @@ pub fn (c &Checker) promote(left_type, right_type table.Type) table.Type {
return left_type // strings, self defined operators
}
if right_type.is_number() && left_type.is_number() {
// sort the operands to save time
mut type_hi := left_type
mut type_lo := right_type
if type_hi.idx() < type_lo.idx() {
tmp := type_hi
type_hi = type_lo
type_lo = tmp
}
idx_hi := type_hi.idx()
idx_lo := type_lo.idx()
// the following comparisons rely on the order of the indices in atypes.v
if idx_hi == table.any_int_type_idx {
return type_lo
} else if idx_hi == table.any_flt_type_idx {
if idx_lo in table.float_type_idxs {
return type_lo
} else {
return table.void_type
}
} else if type_hi.is_float() {
if idx_hi == table.f32_type_idx {
if idx_lo in [table.int_type_idx, table.i64_type_idx, table.u32_type_idx, table.u64_type_idx] {
return table.void_type
} else {
return type_hi
}
} else { // f64, any_flt
if idx_lo in [table.i64_type_idx, table.u64_type_idx] {
return table.void_type
} else {
return type_hi
}
}
} else if idx_lo >= table.byte_type_idx { // both operands are unsigned
return type_hi
} else if idx_lo >= table.i8_type_idx && idx_hi <= table.i64_type_idx { // both signed
return type_hi
} else if idx_hi - idx_lo < (table.byte_type_idx - table.i8_type_idx) {
return type_lo // conversion unsigned -> signed if signed type is larger
} else {
return table.void_type // conversion signed -> unsigned not allowed
}
return c.promote_num(left_type, right_type)
} else {
return left_type // default to left if not automatic promotion possible
}
}
// TODO: promote(), assign_check(), symmetric_check() and check() overlap - should be rearranged
pub fn (c &Checker) assign_check(got, expected table.Type) bool {
fn (c &Checker) promote_num(left_type, right_type table.Type) table.Type {
// sort the operands to save time
mut type_hi := left_type
mut type_lo := right_type
if type_hi.idx() < type_lo.idx() {
type_hi, type_lo = type_lo, type_hi
}
idx_hi := type_hi.idx()
idx_lo := type_lo.idx()
// the following comparisons rely on the order of the indices in atypes.v
if idx_hi == table.any_int_type_idx {
return type_lo
} else if idx_hi == table.any_flt_type_idx {
if idx_lo in table.float_type_idxs {
return type_lo
} else {
return table.void_type
}
} else if type_hi.is_float() {
if idx_hi == table.f32_type_idx {
if idx_lo in [table.int_type_idx, table.i64_type_idx, table.u32_type_idx, table.u64_type_idx] {
return table.void_type
} else {
return type_hi
}
} else { // f64, any_flt
if idx_lo in [table.i64_type_idx, table.u64_type_idx] {
return table.void_type
} else {
return type_hi
}
}
} else if idx_lo >= table.byte_type_idx { // both operands are unsigned
return type_hi
} else if idx_lo >= table.i8_type_idx && idx_hi <= table.i64_type_idx { // both signed
return type_hi
} else if idx_hi - idx_lo < (table.byte_type_idx - table.i8_type_idx) {
return type_lo // conversion unsigned -> signed if signed type is larger
} else {
return table.void_type // conversion signed -> unsigned not allowed
}
}
// TODO: promote(), check_types(), symmetric_check() and check() overlap - should be rearranged
pub fn (c &Checker) check_types(got, expected table.Type) bool {
exp_idx := expected.idx()
got_idx := got.idx()
if exp_idx == got_idx {
@ -228,12 +230,14 @@ pub fn (c &Checker) assign_check(got, expected table.Type) bool {
return true
}
}
if !c.check_types(got, expected) { // TODO: this should go away...
if !c.check_basic(got, expected) { // TODO: this should go away...
return false
}
if c.promote(expected, got) != expected {
println('could not promote ${c.table.get_type_symbol(got).name} to ${c.table.get_type_symbol(expected).name}')
return false
if got.is_number() && expected.is_number() {
if c.promote_num(expected, got) != expected {
// println('could not promote ${c.table.get_type_symbol(got).name} to ${c.table.get_type_symbol(expected).name}')
return false
}
}
return true
}
@ -252,5 +256,5 @@ pub fn (c &Checker) symmetric_check(left, right table.Type) bool {
return true
}
}
return c.check_types(left, right)
return c.check_basic(left, right)
}

View File

@ -369,7 +369,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
expr_type := c.expr(field.expr)
expr_type_sym := c.table.get_type_symbol(expr_type)
field_type_sym := c.table.get_type_symbol(info_field.typ)
if !c.assign_check(expr_type, info_field.typ) {
if !c.check_types(expr_type, info_field.typ) {
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`',
field.pos)
}
@ -698,7 +698,7 @@ fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) {
else {}
}
// Dual sides check (compatibility check)
if !c.assign_check(right_type, left_type) {
if !c.check_types(right_type, left_type) {
left_type_sym := c.table.get_type_symbol(left_type)
right_type_sym := c.table.get_type_symbol(right_type)
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`',
@ -1434,7 +1434,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type {
c.expected_type = elem_type
continue
}
if !c.check_types(elem_type, typ) {
if !c.check_types(typ, elem_type) {
elem_type_sym := c.table.get_type_symbol(elem_type)
c.error('expected array element with type `$elem_type_sym.name`', array_init.pos)
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/function_wrong_arg_type.v:7:7: error: cannot use type `f64` as type `int` in argument 1 to `f`
5 | fn main() {
6 | a := 12.3
7 | q := f(a)
| ~~~~
8 | println('$q')
9 | }

View File

@ -0,0 +1,9 @@
fn f(x int) int {
return x+x
}
fn main() {
a := 12.3
q := f(a)
println('$q')
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/function_wrong_return_type.v:2:9: error: cannot use `any_float` as type `int` in return argument
1 | fn h() int {
2 | return 3.14
| ~~~~
3 | }
4 |

View File

@ -0,0 +1,8 @@
fn h() int {
return 3.14
}
fn main() {
d := h()
println('$d')
}

View File

@ -225,7 +225,7 @@ fn (mut g Gen) jne() int {
pos := g.pos()
g.write32(placeholder)
g.println('jne')
return pos
return int(pos)
}
fn (mut g Gen) jge() int {
@ -233,7 +233,7 @@ fn (mut g Gen) jge() int {
pos := g.pos()
g.write32(placeholder)
g.println('jne')
return pos
return int(pos)
}
fn (mut g Gen) jmp(addr int) {
@ -669,7 +669,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
// The value is the relative address, difference between current position and the location
// after `jne 00 00 00 00`
// println('after if g.pos=$g.pos() jneaddr=$jne_addr')
g.write32_at(jne_addr, g.pos() - jne_addr - 4) // 4 is for "00 00 00 00"
g.write32_at(jne_addr, int(g.pos() - jne_addr - 4)) // 4 is for "00 00 00 00"
}
fn (mut g Gen) for_stmt(node ast.ForStmt) {
@ -690,9 +690,9 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
g.stmts(node.stmts)
// Go back to `cmp ...`
// Diff between `jmp 00 00 00 00 X` and `cmp`
g.jmp(0xffffffff - (g.pos() + 5 - start) + 1)
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
// Update the jump addr to current pos
g.write32_at(jump_addr, g.pos() - jump_addr - 4) // 4 is for "00 00 00 00"
g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) // 4 is for "00 00 00 00"
g.println('jpm after for')
}

View File

@ -23,7 +23,7 @@ fn high_fn_multi_return(a int, b fn (c []int, d []string) ([]int, []string)) {
fn high_fn_return_single_anon() (fn(int)f32) {
_ := 1
correct := fn(n int)f32 {
return n * n
return f32(n * n)
}
return correct
}
@ -36,7 +36,7 @@ fn high_fn_return_multi_anons() (fn(int)f32, fn(int)string) {
return '$n'
}
correct_first := fn(n int)f32 {
return n * n
return f32(n * n)
}
// parsing trap
_ := fn(n int)[]int {
@ -110,7 +110,7 @@ fn simple_fn1() int {
}
fn simple_fn2(n f32) (int, string) {
return 1 + n, "fish"
return int(1 + n), "fish"
}
fn test_assigning_fns() {