gen: more efficient `for in` with a map (#6961)
							parent
							
								
									5e59718970
								
							
						
					
					
						commit
						ea8adfdbf9
					
				| 
						 | 
				
			
			@ -1173,33 +1173,34 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
 | 
			
		|||
	} else if it.kind == .map {
 | 
			
		||||
		// `for key, val in map {`
 | 
			
		||||
		g.writeln('// FOR IN map')
 | 
			
		||||
		key_styp := g.typ(it.key_type)
 | 
			
		||||
		val_styp := g.typ(it.val_type)
 | 
			
		||||
		val_sym := g.table.get_type_symbol(it.val_type)
 | 
			
		||||
		keys_tmp := 'keys_' + g.new_tmp_var()
 | 
			
		||||
		idx := g.new_tmp_var()
 | 
			
		||||
		key := if it.key_var in ['', '_'] { g.new_tmp_var() } else { it.key_var }
 | 
			
		||||
		zero := g.type_default(it.val_type)
 | 
			
		||||
		atmp := g.new_tmp_var()
 | 
			
		||||
		atmp_styp := g.typ(it.cond_type)
 | 
			
		||||
		g.write('$atmp_styp $atmp = ')
 | 
			
		||||
		g.expr(it.cond)
 | 
			
		||||
		g.writeln(';')
 | 
			
		||||
		g.writeln('array_$key_styp $keys_tmp = map_keys(&$atmp);')
 | 
			
		||||
		g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; ++$idx) {')
 | 
			
		||||
		// TODO: analyze whether it.key_type has a .clone() method and call .clone() for all types:
 | 
			
		||||
		if it.key_type == table.string_type {
 | 
			
		||||
			g.writeln('\t$key_styp $key = /*kkkk*/ string_clone( (($key_styp*)${keys_tmp}.data)[$idx] );')
 | 
			
		||||
		} else {
 | 
			
		||||
			g.writeln('\t$key_styp $key = /*kkkk*/ (($key_styp*)${keys_tmp}.data)[$idx];')
 | 
			
		||||
		g.writeln('for (int $idx = 0; $idx < $atmp\.key_values.len; ++$idx) {')
 | 
			
		||||
		g.writeln('\tif ($atmp\.key_values.keys[$idx].str == 0) {continue;}')
 | 
			
		||||
		if it.key_var != '_' {
 | 
			
		||||
			key_styp := g.typ(it.key_type)
 | 
			
		||||
			key := c_name(it.key_var)
 | 
			
		||||
			// TODO: analyze whether it.key_type has a .clone() method and call .clone() for all types:
 | 
			
		||||
			if it.key_type == table.string_type {
 | 
			
		||||
				g.writeln('\t$key_styp $key = /*kkkk*/ string_clone($atmp\.key_values.keys[$idx]);')
 | 
			
		||||
			} else {
 | 
			
		||||
				g.writeln('\t$key_styp $key = /*kkkk*/ $atmp\.key_values.keys[$idx];')
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if it.val_var != '_' {
 | 
			
		||||
			val_sym := g.table.get_type_symbol(it.val_type)
 | 
			
		||||
			valstr := '(void*)($atmp\.key_values.values + $idx * (u32)($atmp\.value_bytes))'
 | 
			
		||||
			if val_sym.kind == .function {
 | 
			
		||||
				g.write('\t')
 | 
			
		||||
				g.write_fn_ptr_decl(val_sym.info as table.FnType, c_name(it.val_var))
 | 
			
		||||
				g.writeln(' = (*(voidptr*)map_get($atmp, $key, &(voidptr[]){ $zero }));')
 | 
			
		||||
				g.writeln(' = (*(voidptr*)$valstr);')
 | 
			
		||||
			} else {
 | 
			
		||||
				g.writeln('\t$val_styp ${c_name(it.val_var)} = (*($val_styp*)map_get($atmp, $key, &($val_styp[]){ $zero }));')
 | 
			
		||||
				val_styp := g.typ(it.val_type)
 | 
			
		||||
				g.writeln('\t$val_styp ${c_name(it.val_var)} = (*($val_styp*)$valstr);')
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		g.stmts(it.stmts)
 | 
			
		||||
| 
						 | 
				
			
			@ -1213,9 +1214,6 @@ fn (mut g Gen) for_in(it ast.ForInStmt) {
 | 
			
		|||
		if it.label.len > 0 {
 | 
			
		||||
			g.writeln('\t$it.label\__break: {}')
 | 
			
		||||
		}
 | 
			
		||||
		g.writeln('/*for in map cleanup*/')
 | 
			
		||||
		g.writeln('for (int $idx = 0; $idx < ${keys_tmp}.len; ++$idx) { string_free(&(($key_styp*)${keys_tmp}.data)[$idx]); }')
 | 
			
		||||
		g.writeln('array_free(&$keys_tmp);')
 | 
			
		||||
		return
 | 
			
		||||
	} else if it.cond_type.has_flag(.variadic) {
 | 
			
		||||
		g.writeln('// FOR IN cond_type/variadic')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,9 +103,11 @@ fn test_for_in_map_val() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn test_for_in_map_both() {
 | 
			
		||||
	mut i := 0
 | 
			
		||||
	for _, _ in m {
 | 
			
		||||
		assert true
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	assert i == 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_nested_for_in_map_key() {
 | 
			
		||||
| 
						 | 
				
			
			@ -127,11 +129,13 @@ fn test_nested_for_in_map_val() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn test_nested_for_in_map_both() {
 | 
			
		||||
	mut i := 0
 | 
			
		||||
	for _, _ in m {
 | 
			
		||||
		for _, _ in m {
 | 
			
		||||
			assert true
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	assert i == 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn fn_for_in_variadic_args_simple(arr ...string) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,16 +43,24 @@ fn test_for_char_in_string() {
 | 
			
		|||
	assert sum == 394 // ascii codes of `a` + `b` + `c` + `d`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_for_char_in_map() {
 | 
			
		||||
fn test_for_string_in_map() {
 | 
			
		||||
	m := {
 | 
			
		||||
		'a': 'b'
 | 
			
		||||
		'c': 'd'
 | 
			
		||||
	}
 | 
			
		||||
	mut acc := ''
 | 
			
		||||
	for k, char in m {
 | 
			
		||||
		acc += '$k: $char, '
 | 
			
		||||
	for k, v in m {
 | 
			
		||||
		acc += '$k: $v, '
 | 
			
		||||
	}
 | 
			
		||||
	assert acc == 'a: b, c: d, '
 | 
			
		||||
 | 
			
		||||
	mut m2 := {'a': 3, 'b': 4, 'c': 5}
 | 
			
		||||
	m2.delete('b')
 | 
			
		||||
	acc = ''
 | 
			
		||||
	for k, v in m2 {
 | 
			
		||||
		acc += '$k: $v, '
 | 
			
		||||
	}
 | 
			
		||||
	assert acc == 'a: 3, c: 5, '
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_mut_for() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue