Merge branch 'master' into unsigned-eq

pull/13967/head
Delyan Angelov 2022-04-11 13:36:31 +03:00 committed by GitHub
commit f8ae042eb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 531 additions and 238 deletions

View File

@ -306,12 +306,14 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
mut run_js := false
is_fmt := ts.vargs.contains('fmt')
is_vet := ts.vargs.contains('vet')
produces_file_output := !(is_fmt || is_vet)
if relative_file.ends_with('js.v') {
if !is_fmt {
if produces_file_output {
cmd_options << ' -b js'
run_js = true
}
run_js = true
}
if relative_file.contains('global') && !is_fmt {
@ -333,13 +335,13 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
fname.replace('.v', '')
}
generated_binary_fpath := os.join_path_single(tmpd, generated_binary_fname)
if os.exists(generated_binary_fpath) {
if ts.rm_binaries {
os.rm(generated_binary_fpath) or {}
if produces_file_output {
if os.exists(generated_binary_fpath) {
if ts.rm_binaries {
os.rm(generated_binary_fpath) or {}
}
}
}
if !ts.vargs.contains('fmt') {
cmd_options << ' -o ${os.quoted_path(generated_binary_fpath)}'
}
cmd := '${os.quoted_path(ts.vexe)} ' + cmd_options.join(' ') + ' ${os.quoted_path(file)}'
@ -421,10 +423,8 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
}
}
}
if os.exists(generated_binary_fpath) {
if ts.rm_binaries {
os.rm(generated_binary_fpath) or {}
}
if produces_file_output && os.exists(generated_binary_fpath) && ts.rm_binaries {
os.rm(generated_binary_fpath) or {}
}
return pool.no_result
}

View File

@ -247,12 +247,8 @@ fn (mut foptions FormatOptions) post_process_file(file string, formatted_file_pa
if !is_formatted_different {
return
}
x := diff.color_compare_files(foptions.find_diff_cmd(), file, formatted_file_path)
if x.len != 0 {
println("$file is not vfmt'ed")
return error('')
}
return
println("$file is not vfmt'ed")
return error('')
}
if foptions.is_c {
if is_formatted_different {

View File

@ -87,6 +87,12 @@ fn main() {
module_names = manifest.dependencies
}
mut source := Source.vpm
if '--once' in options {
module_names = vpm_once_filter(module_names)
if module_names.len == 0 {
return
}
}
if '--git' in options {
source = Source.git
}
@ -327,6 +333,17 @@ fn vpm_install_from_vcs(module_names []string, vcs_key string) {
}
}
fn vpm_once_filter(module_names []string) []string {
installed_modules := get_installed_modules()
mut toinstall := []string{}
for mn in module_names {
if mn !in installed_modules {
toinstall << mn
}
}
return toinstall
}
fn vpm_install(module_names []string, source Source) {
if settings.is_help {
vhelp.show_topic('install')
@ -336,15 +353,16 @@ fn vpm_install(module_names []string, source Source) {
println('´v install´ requires *at least one* module name.')
exit(2)
}
if source == .vpm {
vpm_install_from_vpm(module_names)
}
if source == .git {
vpm_install_from_vcs(module_names, 'git')
}
if source == .hg {
vpm_install_from_vcs(module_names, 'hg')
match source {
.vpm {
vpm_install_from_vpm(module_names)
}
.git {
vpm_install_from_vcs(module_names, 'git')
}
.hg {
vpm_install_from_vcs(module_names, 'hg')
}
}
}
@ -445,12 +463,11 @@ fn vpm_outdated() {
fn vpm_list() {
module_names := get_installed_modules()
if module_names.len == 0 {
println('You have no modules installed.')
eprintln('You have no modules installed.')
exit(0)
}
println('Installed modules:')
for mod in module_names {
println(' $mod')
println(mod)
}
}

View File

@ -8,6 +8,7 @@ Options:
--vpm - [Default] Install from vpm
--git - Install from git repository url
--hg - Install from mercurial repository url
--once - Only install the module if it was not previously installed
-help - Show usage info.
-v - Print more details about the performed operation.
-server-url - When doing network operations, use this vpm server. Can be given multiple times.

View File

@ -26,6 +26,8 @@ git clone https://github.com/vlang/v
cd v
make
```
See [here](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Linux-and-macOS)
for how to install the development tools.
### Windows:
You need `git`, and a C compiler like `tcc`, `gcc`, `clang` or `msvc`:
@ -38,6 +40,9 @@ NB: You can also pass one of `-gcc`, `-msvc`, `-clang` to `make.bat` instead,
if you do prefer to use a different C compiler, but -tcc is small, fast, and
easy to install (V will download a prebuilt binary automatically).
For C compiler downloads and more info, see
[here](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows).
It is recommended to add this folder to the PATH of your environment variables.
This can be done with the command `v.exe symlink`.
@ -4601,13 +4606,19 @@ v install ui
Modules can be installed directly from git or mercurial repositories.
```powershell
v install [--git|--hg] [url]
v install [--once] [--git|--hg] [url]
```
**Example:**
```powershell
v install --git https://github.com/vlang/markdown
```
Sometimes you may want to install the dependencies **ONLY** if those are not installed:
```
v install --once [module]
```
Removing a module with v:
```powershell

View File

@ -30,13 +30,10 @@ fn main() {
println('failed to fetch data from /v0/topstories.json')
return
}
mut ids := json.decode([]int, resp.text) or {
ids := json.decode([]int, resp.text) or {
println('failed to decode topstories.json')
return
}
if ids.len > 10 {
ids = ids[0..10]
}
}#[0..10]
mut fetcher_pool := pool.new_pool_processor(
callback: worker_fetch
)

View File

@ -17,6 +17,8 @@ fn main() {
}
title := 'Sokol Drawing Template'
desc := sapp.Desc{
width: 640
height: 480
user_data: state
init_userdata_cb: init
frame_userdata_cb: frame
@ -49,9 +51,9 @@ fn draw() {
sgl.matrix_mode_projection()
sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0)
sgl.c4b(255, 0, 0, 128)
draw_hollow_rect(10, 10, 100, 30)
sgl.c4b(25, 150, 0, 128)
draw_filled_rect(10, 150, 80, 40)
draw_hollow_rect(220, 140, 200, 200)
sgl.c4b(25, 150, 255, 128)
draw_filled_rect(270, 190, 100, 100)
// line(0, 0, 500, 500)
}

View File

@ -293,3 +293,10 @@ fn test_can_copy_bits() {
// map not copyable
assert !can_copy_bits<map[string]int>()
}
type Str = string
fn test_alias_string_contains() {
names := [Str('')]
assert (Str('') in names) == true
}

View File

@ -330,7 +330,7 @@ fn (mut cb Clipboard) start_listener() {
property: xsre.property
}
if !cb.transmit_selection(&xse) {
xse.property = new_atom(0)
xse.property = Atom(0)
}
C.XSendEvent(cb.display, xse.requestor, 0, C.PropertyChangeMask, voidptr(&xse))
C.XFlush(cb.display)
@ -479,10 +479,6 @@ fn (cb &Clipboard) get_supported_targets() []Atom {
return cb.get_atoms(AtomType.utf8_string, .xa_string, .text, .text_plain, .text_html)
}
fn new_atom(value int) &Atom {
return unsafe { &Atom(&u64(u64(value))) }
}
fn create_xwindow(display &C.Display) Window {
n := C.DefaultScreen(display)
return C.XCreateSimpleWindow(display, C.RootWindow(display, n), 0, 0, 1, 1, 0, C.BlackPixel(display,

View File

@ -24,25 +24,34 @@ pub type Context = C.FONScontext
pub const (
// TODO: fontstash.used_import is used to keep v from warning about unused imports
used_import = 1
invalid = C.FONS_INVALID // -1
)
// Contructor and destructor.
// create_internal returns a fontstash Context allocated on the heap.
//
// See also: delete_internal
[inline]
pub fn create_internal(params &C.FONSparams) &Context {
return C.fonsCreateInternal(params)
}
// delete_internal deletes and free memory of `s` fontstash Context.
//
// See also: create_internal
[inline]
pub fn delete_internal(s &Context) {
C.fonsDeleteInternal(s)
}
// set_error_callback sets `callback` as a function to be called if fontstash
// encounter any errors. `uptr` can be used to pass custom userdata.
[inline]
pub fn (s &Context) set_error_callback(callback fn (voidptr, int, int), uptr voidptr) {
C.fonsSetErrorCallback(s, callback, uptr)
}
// Returns current atlas size.
// get_atlas_size returns the current size of the texture atlas which
// the font is rendered to.
[inline]
pub fn (s &Context) get_atlas_size() (int, int) {
mut width := 0
@ -51,126 +60,203 @@ pub fn (s &Context) get_atlas_size() (int, int) {
return width, height
}
// Expands the atlas size.
// expand_atlas expands the font texture atlas size to `width` x `height`.
[inline]
pub fn (s &Context) expand_atlas(width int, height int) int {
return C.fonsExpandAtlas(s, width, height)
}
// Resets the whole stash.
// reset_atlas resets `width` x `height` of the font texture atlas.
[inline]
pub fn (s &Context) reset_atlas(width int, height int) int {
return C.fonsResetAtlas(s, width, height)
}
// Add fonts
// get_font_by_name returns the id of the font with `name` or
// `fontstash.invalid` if no font with `name` could be found.
[inline]
pub fn (s &Context) get_font_by_name(name string) int {
return C.fonsGetFontByName(s, &char(name.str))
}
// add_fallback_font adds a fallback font to the `base` font id in the Context.
// `fallback` is expected to be the id of a previous, successfully, added font.
// add_fallback_font returns `1` on success, `0` otherwise.
[inline]
pub fn (s &Context) add_fallback_font(base int, fallback int) int {
return C.fonsAddFallbackFont(s, base, fallback)
}
// add_font_mem adds the font data located in memory to the Context.
// `name` is the human readable name for the font.
// `free_data` indicates if `data` should be freed after the font is added.
// The function returns the id of the font on success, `fontstash.invalid` otherwise.
[inline]
pub fn (s &Context) add_font_mem(name string, data []byte, free_data bool) int {
return C.fonsAddFontMem(s, &char(name.str), data.data, data.len, int(free_data))
}
// State handling
// push_state pushes a new state on the state stack.
// A state holds the current attributes of the rendering,
// attributes are things like color, size, the font in use, blur effect etc.
//
// See also: pop_state
// See also: clear_state
// See also: set_size
// See also: set_color
// See also: set_spacing
// See also: set_blur
// See also: set_align
// See also: set_font
[inline]
pub fn (s &Context) push_state() {
C.fonsPushState(s)
}
// pop_state pops the current state from the state stack.
//
// See also: push_state
// See also: clear_state
[inline]
pub fn (s &Context) pop_state() {
C.fonsPopState(s)
}
// clear_state clears the current state.
//
// See also: push_state
// See also: pop_state
[inline]
pub fn (s &Context) clear_state() {
C.fonsClearState(s)
}
// State setting
// set_size sets the font size to `size` on the active state.
//
// See also: push_state
// See also: pop_state
// See also: clear_state
[inline]
pub fn (s &Context) set_size(size f32) {
C.fonsSetSize(s, size)
}
// set_color sets the font color to `color` on the active state.
//
// See also: push_state
// See also: pop_state
// See also: clear_state
[inline]
pub fn (s &Context) set_color(color u32) {
C.fonsSetColor(s, color)
}
// set_spacing sets the font spacing to `spacing` on the active state.
//
// See also: push_state
// See also: pop_state
// See also: clear_state
[inline]
pub fn (s &Context) set_spacing(spacing f32) {
C.fonsSetSpacing(s, spacing)
}
// set_blur sets the font blur effect to `blur` on the active state.
//
// See also: push_state
// See also: pop_state
// See also: clear_state
[inline]
pub fn (s &Context) set_blur(blur f32) {
C.fonsSetBlur(s, blur)
}
// set_align sets the font aligning to `align` on the active state.
//
// See also: push_state
// See also: pop_state
// See also: clear_state
[inline]
pub fn (s &Context) set_align(align int) {
C.fonsSetAlign(s, align)
}
// set_font sets the font used for this render on the active state.
// `font_id` is the id of the loaded font.
//
// See also: push_state
// See also: pop_state
// See also: clear_state
[inline]
pub fn (s &Context) set_font(font int) {
C.fonsSetFont(s, font)
pub fn (s &Context) set_font(font_id int) {
C.fonsSetFont(s, font_id)
}
// Draw text
// draw_text draws the `text` string at position `x`,`y`.
// The function returns the `x` coordinate of the resulting render.
[inline]
pub fn (s &Context) draw_text(x f32, y f32, text string) f32 {
return C.fonsDrawText(s, x, y, &char(text.str), &char(0))
}
// Measure text
// text_bounds fills the `bounds` argument with the pixel dimensions
// of the rendered `text` at position `x`,`y`.
//
// `bounds` is expected to be of type `mut bounds := [4]f32{}`.
// Call example: `ctx.text_bounds(0, 0, 'example', &bounds[0])`.
// `bounds[0]` is the `x` coordinate of the top-left point.
// `bounds[1]` is the `y` coordinate of the top-left point.
// `bounds[2]` is the `x` coordinate of the bottom-right point.
// `bounds[3]` is the `y` coordinate of the bottom-right point.
[inline]
pub fn (s &Context) text_bounds(x f32, y f32, text string, bounds &f32) f32 {
return C.fonsTextBounds(s, x, y, &char(text.str), &char(0), bounds)
}
// line_bounds fills `miny` and `maxy` with the values of the `minimum`
// and `maximum` line bounds respectively.
[inline]
pub fn (s &Context) line_bounds(y f32, miny &f32, maxy &f32) {
C.fonsLineBounds(s, y, miny, maxy)
}
// vert_metrics assigns the respective values of `ascender`, `descender` and `lineh`.
[inline]
pub fn (s &Context) vert_metrics(ascender &f32, descender &f32, lineh &f32) {
C.fonsVertMetrics(s, ascender, descender, lineh)
}
// Text iterator
// text_iter_init initalizes the text iterator `iter`.
[inline]
pub fn (s &Context) text_iter_init(iter &C.FONStextIter, x f32, y f32, str &char, end &char) int {
return C.fonsTextIterInit(s, iter, x, y, str, end)
}
// text_iter_next advances `iter` to the next `quad`.
[inline]
pub fn (s &Context) text_iter_next(iter &C.FONStextIter, quad &C.FONSquad) int {
return C.fonsTextIterNext(s, iter, quad)
}
// Pull texture changes
// get_texture_data returns the current Context's raw texture data.
// `width` and `height` is assigned the size of the texture dimensions.
[inline]
pub fn (s &Context) get_texture_data(width &int, height &int) &byte {
return &byte(C.fonsGetTextureData(s, width, height))
}
// validate_texture fills the `dirty` argument with the pixel dimensions
// of the dirty rectangle of the Context's raw texture, if any.
//
// `dirty` is expected to be of type `mut dirty := [4]int{}`.
// Call example: `is_dirty := ctx.validate_texture(&dirty[0])`.
// The function returns `1` if the texture has a dirty rectangle, `0` otherwise.
[inline]
pub fn (s &Context) validate_texture(dirty &int) int {
return C.fonsValidateTexture(s, dirty)
}
// Draws the stash texture for debugging
// draw_debug draws the stash texture for debugging.
[inline]
pub fn (s &Context) draw_debug(x f32, y f32) {
C.fonsDrawDebug(s, x, y)

View File

@ -1,9 +1,7 @@
import json
import os
struct DbConfig {
foo int
}
struct DbConfig {}
fn test_json_decode_with_optional_arg() {
if ret := print_info() {

View File

@ -332,7 +332,10 @@ pub fn execute(cmd string) Result {
// if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') {
// return Result{ exit_code: -1, output: ';, &&, || and \\n are not allowed in shell commands' }
// }
pcmd := if cmd.contains('2>') { cmd } else { '$cmd 2>&1' }
pcmd := if cmd.contains('2>') { cmd.clone() } else { '$cmd 2>&1' }
defer {
unsafe { pcmd.free() }
}
f := vpopen(pcmd)
if isnil(f) {
return Result{

View File

@ -144,24 +144,25 @@ pub fn (mut rng MT19937RNG) u32() u32 {
return u32(ans)
}
const mag01 = [u64(0), u64(matrix_a)]
// u64 returns a pseudorandom 64bit int in range `[0, 2)`.
[inline]
[direct_array_access; inline]
pub fn (mut rng MT19937RNG) u64() u64 {
mag01 := [u64(0), u64(mt19937.matrix_a)]
mut x := u64(0)
mut i := int(0)
if rng.mti >= mt19937.nn {
for i = 0; i < mt19937.nn - mt19937.mm; i++ {
x = (rng.state[i] & mt19937.um) | (rng.state[i + 1] & mt19937.lm)
rng.state[i] = rng.state[i + mt19937.mm] ^ (x >> 1) ^ mag01[int(x & 1)]
rng.state[i] = rng.state[i + mt19937.mm] ^ (x >> 1) ^ mt19937.mag01[int(x & 1)]
}
for i < mt19937.nn - 1 {
x = (rng.state[i] & mt19937.um) | (rng.state[i + 1] & mt19937.lm)
rng.state[i] = rng.state[i + (mt19937.mm - mt19937.nn)] ^ (x >> 1) ^ mag01[int(x & 1)]
rng.state[i] = rng.state[i + (mt19937.mm - mt19937.nn)] ^ (x >> 1) ^ mt19937.mag01[int(x & 1)]
i++
}
x = (rng.state[mt19937.nn - 1] & mt19937.um) | (rng.state[0] & mt19937.lm)
rng.state[mt19937.nn - 1] = rng.state[mt19937.mm - 1] ^ (x >> 1) ^ mag01[int(x & 1)]
rng.state[mt19937.nn - 1] = rng.state[mt19937.mm - 1] ^ (x >> 1) ^ mt19937.mag01[int(x & 1)]
rng.mti = 0
}
x = rng.state[rng.mti]

View File

@ -29,7 +29,7 @@ pub fn (mut rng MuslRNG) seed(seed_data []u32) {
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
[inline]
fn (mut rng MuslRNG) byte() byte {
pub fn (mut rng MuslRNG) byte() byte {
if rng.bytes_left >= 1 {
rng.bytes_left -= 1
value := byte(rng.buffer)
@ -70,7 +70,7 @@ fn temper(prev u32) u32 {
}
// u32 returns a pseudorandom 32-bit unsigned integer (`u32`).
fn (mut rng MuslRNG) u32() u32 {
pub fn (mut rng MuslRNG) u32() u32 {
rng.state = rng.state * 1103515245 + 12345
// We are not dividing by 2 (or shifting right by 1)
// because we want all 32-bits of random data

View File

@ -39,7 +39,7 @@ pub fn (mut rng PCG32RNG) seed(seed_data []u32) {
// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`.
[inline]
fn (mut rng PCG32RNG) byte() byte {
pub fn (mut rng PCG32RNG) byte() byte {
if rng.bytes_left >= 1 {
rng.bytes_left -= 1
value := byte(rng.buffer)
@ -70,7 +70,7 @@ pub fn (mut rng PCG32RNG) u16() u16 {
// u32 returns a pseudorandom unsigned `u32`.
[inline]
fn (mut rng PCG32RNG) u32() u32 {
pub fn (mut rng PCG32RNG) u32() u32 {
oldstate := rng.state
rng.state = oldstate * (6364136223846793005) + rng.inc
xorshifted := u32(((oldstate >> u64(18)) ^ oldstate) >> u64(27))

View File

@ -1,26 +1,47 @@
module strconv
/*
atof util
// Copyright (c) 2019-2022 Dario Deledda. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// This file contains utilities for converting a string to a f64 variable.
// IEEE 754 standard is used.
// Know limitation: limited to 18 significant digits
//
// The code is inspired by:
// Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl
// URL: http://krashan.ppa.pl/articles/stringtofloat/
// Original license: MIT
// 96 bit operation utilities
//
// Note: when u128 will be available, these function can be refactored.
Copyright (c) 2019-2022 Dario Deledda. All rights reserved.
Use of this source code is governed by an MIT license
that can be found in the LICENSE file.
// f32 constants
pub const (
single_plus_zero = u32(0x0000_0000)
single_minus_zero = u32(0x8000_0000)
single_plus_infinity = u32(0x7F80_0000)
single_minus_infinity = u32(0xFF80_0000)
)
This file contains utilities for convert a string in a f64 variable
IEEE 754 standard is used
// f64 constants
pub const (
digits = 18
double_plus_zero = u64(0x0000000000000000)
double_minus_zero = u64(0x8000000000000000)
double_plus_infinity = u64(0x7FF0000000000000)
double_minus_infinity = u64(0xFFF0000000000000)
)
Know limitation:
- limited to 18 significant digits
The code is inspired by:
Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl
URL: http://krashan.ppa.pl/articles/stringtofloat/
Original license: MIT
96 bit operation utilities
Note: when u128 will be available these function can be refactored
*/
// char constants
pub const (
c_dpoint = `.`
c_plus = `+`
c_minus = `-`
c_zero = `0`
c_nine = `9`
c_ten = u32(10)
)
// right logical shift 96 bit
fn lsr96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) {
@ -78,48 +99,7 @@ fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) {
return r2, r1, r0
}
// Constants
pub const (
//
// f32 constants
//
single_plus_zero = u32(0x0000_0000)
single_minus_zero = u32(0x8000_0000)
single_plus_infinity = u32(0x7F80_0000)
single_minus_infinity = u32(0xFF80_0000)
//
// f64 constants
//
digits = 18
double_plus_zero = u64(0x0000000000000000)
double_minus_zero = u64(0x8000000000000000)
double_plus_infinity = u64(0x7FF0000000000000)
double_minus_infinity = u64(0xFFF0000000000000)
//
// Possible parser return values.
//
parser_ok = 0 // parser finished OK
parser_pzero = 1 // no digits or number is smaller than +-2^-1022
parser_mzero = 2 // number is negative, module smaller
parser_pinf = 3 // number is higher than +HUGE_VAL
parser_minf = 4 // number is lower than -HUGE_VAL
parser_invalid_number = 5 // invalid number, used for '#@%^' for example
//
// char constants
// Note: Modify these if working with non-ASCII encoding
//
c_dpoint = `.`
c_plus = `+`
c_minus = `-`
c_zero = `0`
c_nine = `9`
c_ten = u32(10)
)
// Utility functions
// NOTE: Modify these if working with non-ASCII encoding
fn is_digit(x byte) bool {
return (x >= strconv.c_zero && x <= strconv.c_nine) == true
}
@ -132,14 +112,21 @@ fn is_exp(x byte) bool {
return (x == `E` || x == `e`) == true
}
/*
String parser
NOTE: #TOFIX need one char after the last char of the number
*/
// Possible parser return values.
enum ParserState {
ok // parser finished OK
pzero // no digits or number is smaller than +-2^-1022
mzero // number is negative, module smaller
pinf // number is higher than +HUGE_VAL
minf // number is lower than -HUGE_VAL
invalid_number // invalid number, used for '#@%^' for example
}
fn parser(s string) (int, PrepNumber) {
// parser tries to parse the given string into a number
// NOTE: #TOFIX need one char after the last char of the number
fn parser(s string) (ParserState, PrepNumber) {
mut digx := 0
mut result := strconv.parser_ok
mut result := ParserState.ok
mut expneg := false
mut expexp := 0
mut i := 0
@ -216,45 +203,45 @@ fn parser(s string) (int, PrepNumber) {
pn.exponent += expexp
if pn.mantissa == 0 {
if pn.negative {
result = strconv.parser_mzero
result = .mzero
} else {
result = strconv.parser_pzero
result = .pzero
}
} else if pn.exponent > 309 {
if pn.negative {
result = strconv.parser_minf
result = .minf
} else {
result = strconv.parser_pinf
result = .pinf
}
} else if pn.exponent < -328 {
if pn.negative {
result = strconv.parser_mzero
result = .mzero
} else {
result = strconv.parser_pzero
result = .pzero
}
}
if i == 0 && s.len > 0 {
return strconv.parser_invalid_number, pn
return ParserState.invalid_number, pn
}
return result, pn
}
/*
Converter to the bit form of the f64 number
*/
// converter return a u64 with the bit image of the f64 number
// converter returns a u64 with the bit image of the f64 number
fn converter(mut pn PrepNumber) u64 {
mut binexp := 92
mut s2 := u32(0) // 96-bit precision integer
// s0,s1,s2 are the parts of a 96-bit precision integer
mut s2 := u32(0)
mut s1 := u32(0)
mut s0 := u32(0)
mut q2 := u32(0) // 96-bit precision integer
// q0,q1,q2 are the parts of a 96-bit precision integer
mut q2 := u32(0)
mut q1 := u32(0)
mut q0 := u32(0)
mut r2 := u32(0) // 96-bit precision integer
// r0,r1,r2 are the parts of a 96-bit precision integer
mut r2 := u32(0)
mut r1 := u32(0)
mut r0 := u32(0)
//
mask28 := u32(u64(0xF) << 28)
mut result := u64(0)
// working on 3 u32 to have 96 bit precision
@ -404,35 +391,30 @@ fn converter(mut pn PrepNumber) u64 {
return result
}
// Public functions
// atof64 return a f64 from a string doing a parsing operation
// atof64 parses the string `s`, and if possible, converts it into a f64 number
pub fn atof64(s string) ?f64 {
if s.len == 0 {
return error('expected a number found an empty string')
}
mut pn := PrepNumber{}
mut res_parsing := 0
mut res := Float64u{}
res_parsing, pn = parser(s)
mut res_parsing, mut pn := parser(s)
match res_parsing {
strconv.parser_ok {
.ok {
res.u = converter(mut pn)
}
strconv.parser_pzero {
.pzero {
res.u = strconv.double_plus_zero
}
strconv.parser_mzero {
.mzero {
res.u = strconv.double_minus_zero
}
strconv.parser_pinf {
.pinf {
res.u = strconv.double_plus_infinity
}
strconv.parser_minf {
.minf {
res.u = strconv.double_minus_infinity
}
else {
.invalid_number {
return error('not a number')
}
}

View File

@ -142,11 +142,11 @@ pub fn h_divider(divider string) string {
// ==== TITLE =========================
pub fn header_left(text string, divider string) string {
plain_text := strip_ansi(text)
xcols, _ := get_terminal_size()
xcols, _ := get_terminal_size() // can get 0 in lldb/gdb
cols := imax(1, xcols)
relement := if divider.len > 0 { divider } else { ' ' }
hstart := relement.repeat(4)[0..4]
remaining_cols := (cols - (hstart.len + 1 + plain_text.len + 1))
remaining_cols := imax(0, (cols - (hstart.len + 1 + plain_text.len + 1)))
hend := relement.repeat((remaining_cols + 1) / relement.len)[0..remaining_cols]
return '$hstart $text $hend'
}

View File

@ -3,14 +3,13 @@
// that can be found in the LICENSE file.
module scanner
import math
import toml.input
import toml.token
import toml.util
pub const (
digit_extras = [`_`, `.`, `x`, `o`, `b`, `e`, `E`]
end_of_text = math.max_u32
end_of_text = 4294967295
)
// Scanner contains the necessary fields for the state of the scan process.
@ -346,7 +345,7 @@ fn (mut s Scanner) new_token(kind token.Kind, lit string, len int) token.Token {
return token.Token{
kind: kind
lit: lit
col: math.max(1, col)
col: if col < 1 { 1 } else { col }
line_nr: s.line_nr + 1
pos: s.pos - s.header_len - len + 1
len: len

View File

@ -1175,6 +1175,13 @@ fn (t Table) shorten_user_defined_typenames(originalname string, import_aliases
} else if res in import_aliases {
res = import_aliases[res]
} else {
// FIXME: clean this case and remove the following if
// because it is an hack to format well the type when
// there is a []modul.name
if res.contains('[]') {
idx := res.index('.') or { -1 }
return res[idx + 1..]
}
// types defined by the user
// mod.submod.submod2.Type => submod2.Type
mut parts := res.split('.')

View File

@ -124,6 +124,7 @@ mut:
inside_println_arg bool
inside_decl_rhs bool
inside_if_guard bool // true inside the guard condition of `if x := opt() {}`
comptime_call_pos int // needed for correctly checking use before decl for templates
}
pub fn new_checker(table &ast.Table, pref &pref.Preferences) &Checker {
@ -3127,9 +3128,16 @@ pub fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
return obj.typ
}
ast.Var {
// incase var was not marked as used yet (vweb tmpl)
// obj.is_used = true
if node.pos.pos < obj.pos.pos {
// inside vweb tmpl ident positions are meaningless, use the position of the comptime call.
// if the variable is declared before the comptime call then we can assume all is well.
// `node.name !in node.scope.objects && node.scope.start_pos < c.comptime_call_pos` (inherited)
node_pos := if c.pref.is_vweb && node.name !in node.scope.objects
&& node.scope.start_pos < c.comptime_call_pos {
c.comptime_call_pos
} else {
node.pos.pos
}
if node_pos < obj.pos.pos {
c.error('undefined variable `$node.name` (used before declaration)',
node.pos)
}

View File

@ -36,28 +36,8 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
is_vweb: true
}
mut c2 := new_checker(c.table, pref2)
c2.comptime_call_pos = node.pos.pos
c2.check(node.vweb_tmpl)
mut caller_scope := c.fn_scope.innermost(node.pos.pos)
mut i := 0 // tmp counter var for skipping first three tmpl vars
for k, _ in c2.file.scope.children[0].objects {
if i < 2 {
// Skip first three because they are tmpl vars see vlib/vweb/tmpl/tmpl.v
i++
continue
}
tmpl_obj := unsafe { c2.file.scope.children[0].objects[k] }
if tmpl_obj is ast.Var {
if mut caller_var := caller_scope.find_var(tmpl_obj.name) {
// var is used in the tmpl so mark it as used in the caller
caller_var.is_used = true
// update props from the caller scope var to the tmpl scope var
c2.file.scope.children[0].objects[k] = ast.Var{
...(*caller_var)
pos: tmpl_obj.pos
}
}
}
}
c.warnings << c2.warnings
c.errors << c2.errors
c.notices << c2.notices

View File

@ -9,6 +9,22 @@ pub fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
mut elem_type := ast.void_type
// []string - was set in parser
if node.typ != ast.void_type {
if node.elem_type != 0 {
elem_sym := c.table.sym(node.elem_type)
if elem_sym.kind == .struct_ {
elem_info := elem_sym.info as ast.Struct
if elem_info.generic_types.len > 0 && elem_info.concrete_types.len == 0
&& !node.elem_type.has_flag(.generic) {
if c.table.cur_concrete_types.len == 0 {
c.error('generic struct must specify type parameter, e.g. Foo<int>',
node.elem_type_pos)
} else {
c.error('generic struct must specify type parameter, e.g. Foo<T>',
node.elem_type_pos)
}
}
}
}
if node.exprs.len == 0 {
if node.has_cap {
c.check_array_init_para_type('cap', node.cap_expr, node.pos)
@ -244,6 +260,22 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
// `x := map[string]string` - set in parser
if node.typ != 0 {
info := c.table.sym(node.typ).map_info()
if info.value_type != 0 {
val_sym := c.table.sym(info.value_type)
if val_sym.kind == .struct_ {
val_info := val_sym.info as ast.Struct
if val_info.generic_types.len > 0 && val_info.concrete_types.len == 0
&& !info.value_type.has_flag(.generic) {
if c.table.cur_concrete_types.len == 0 {
c.error('generic struct `$val_sym.name` must specify type parameter, e.g. Foo<int>',
node.pos)
} else {
c.error('generic struct `$val_sym.name` must specify type parameter, e.g. Foo<T>',
node.pos)
}
}
}
}
c.ensure_type_exists(info.key_type, node.pos) or {}
c.ensure_type_exists(info.value_type, node.pos) or {}
node.key_type = info.key_type

View File

@ -1726,6 +1726,10 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, node ast
if is_map && arg_expr.return_type in [ast.void_type, 0] {
c.error('type mismatch, `$arg_expr.name` does not return anything', arg_expr.pos)
} else if !is_map && arg_expr.return_type != ast.bool_type {
if arg_expr.or_block.kind != .absent && arg_expr.return_type.has_flag(.optional)
&& arg_expr.return_type.clear_flag(.optional) == ast.bool_type {
return
}
c.error('type mismatch, `$arg_expr.name` must return a bool', arg_expr.pos)
}
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/array_of_generic_struct_init_err.vv:6:15: error: generic struct must specify type parameter, e.g. Foo<int>
4 |
5 | fn main() {
6 | mut arr := []Item{}
| ~~~~
7 | }

View File

@ -0,0 +1,7 @@
struct Item<T>{
val T
}
fn main() {
mut arr := []Item{}
}

View File

@ -0,0 +1,27 @@
vlib/v/checker/tests/check_err_msg_with_generics.vv:15:10: error: cannot cast struct `BSTree<Result<[]Token, Err<string>>>` to `int`
13 | fn test_err_msg() {
14 | typ := datatypes.BSTree<Result<[]Token, Err<string>>>{}
15 | println(int(typ))
| ~~~~~~~~
16 | }
vlib/datatypes/bstree.v:190:17: error: cannot append `T` to `[]T`
188 | }
189 | bst.in_order_traversal_helper(node.left, mut result)
190 | result << node.value
| ~~~~~
191 | bst.in_order_traversal_helper(node.right, mut result)
192 | }
vlib/datatypes/bstree.v:210:17: error: cannot append `T` to `[]T`
208 | bst.post_order_traversal_helper(node.left, mut result)
209 | bst.post_order_traversal_helper(node.right, mut result)
210 | result << node.value
| ~~~~~
211 | }
212 |
vlib/datatypes/bstree.v:226:17: error: cannot append `T` to `[]T`
224 | return
225 | }
226 | result << node.value
| ~~~~~
227 | bst.pre_order_traversal_helper(node.left, mut result)
228 | bst.pre_order_traversal_helper(node.right, mut result)

View File

@ -0,0 +1,16 @@
import datatypes
type Result<T, U> = Err<U> | Ok<T>
struct Ok<T> {
value T
}
struct Err<U> {
value U
}
fn test_err_msg() {
typ := datatypes.BSTree<Result<[]Token, Err<string>>>{}
println(int(typ))
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/map_of_generic_struct_init_err.vv:6:11: error: generic struct `Item` must specify type parameter, e.g. Foo<int>
4 |
5 | fn main() {
6 | mut m := map[string]Item{}
| ~~~~~~~~~~~~~~~~~
7 | }

View File

@ -0,0 +1,7 @@
struct Item<T>{
val T
}
fn main() {
mut m := map[string]Item{}
}

View File

@ -0,0 +1,13 @@
vlib/v/checker/tests/struct_field_name_err.vv:2:2: error: field name `Architecture` cannot contain uppercase letters, use snake_case instead
1 | struct Release {
2 | Architecture []string
| ~~~~~~~~~~~~~~~~~~~~~
3 | Components []string
4 | }
vlib/v/checker/tests/struct_field_name_err.vv:3:2: error: field name `Components` cannot contain uppercase letters, use snake_case instead
1 | struct Release {
2 | Architecture []string
3 | Components []string
| ~~~~~~~~~~~~~~~~~~~~~
4 | }
5 |

View File

@ -0,0 +1,9 @@
struct Release {
Architecture []string
Components []string
}
fn main() {
r := Release{}
println(r)
}

View File

@ -673,7 +673,11 @@ fn (mut g Gen) gen_array_contains_methods() {
fn_name := '${left_type_str}_contains'
left_info := left_final_sym.info as ast.Array
mut elem_type_str := g.typ(left_info.elem_type)
elem_sym := g.table.sym(left_info.elem_type)
mut elem_sym := g.table.sym(left_info.elem_type)
if elem_sym.kind == .alias {
info := elem_sym.info as ast.Alias
elem_sym = g.table.sym(info.parent_type)
}
if elem_sym.kind == .function {
left_type_str = 'Array_voidptr'
elem_type_str = 'voidptr'
@ -751,7 +755,11 @@ fn (mut g Gen) gen_array_index_methods() {
fn_name := '${left_type_str}_index'
info := final_left_sym.info as ast.Array
mut elem_type_str := g.typ(info.elem_type)
elem_sym := g.table.sym(info.elem_type)
mut elem_sym := g.table.sym(info.elem_type)
if elem_sym.kind == .alias {
info_t := elem_sym.info as ast.Alias
elem_sym = g.table.sym(info_t.parent_type)
}
if elem_sym.kind == .function {
left_type_str = 'Array_voidptr'
elem_type_str = 'voidptr'

View File

@ -238,11 +238,12 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
} else {
g.out.go_back_to(pos)
is_var_mut := !is_decl && left.is_auto_deref_var()
addr := if is_var_mut { '' } else { '&' }
addr_left := if is_var_mut { '' } else { '&' }
g.writeln('')
g.write('memcpy($addr')
g.write('memcpy($addr_left')
g.expr(left)
g.writeln(', &$v_var, sizeof($arr_typ));')
addr_val := if is_fixed_array_var { '' } else { '&' }
g.writeln(', $addr_val$v_var, sizeof($arr_typ));')
}
g.is_assign_lhs = false
} else {

View File

@ -972,7 +972,7 @@ fn (g Gen) optional_type_text(styp string, base string) string {
ret := 'struct $styp {
byte state;
IError err;
byte data[sizeof($size)];
byte data[sizeof($size) > 0 ? sizeof($size) : 1];
}'
return ret
}
@ -2645,12 +2645,13 @@ fn (mut g Gen) autofree_variable(v ast.Var) {
g.autofree_var_call('string_free', v)
return
}
if sym.has_method('free') {
g.autofree_var_call(free_fn, v)
} else if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() {
if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() {
// Free user reference types
g.autofree_var_call('free', v)
}
if sym.has_method('free') {
g.autofree_var_call(free_fn, v)
}
}
fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
@ -2698,7 +2699,11 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
if v.typ == ast.error_type && !v.is_autofree_tmp {
return
}
af.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var $g.cur_mod.name $g.is_builtin_mod')
if v.is_auto_heap {
af.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed heap var $g.cur_mod.name $g.is_builtin_mod')
} else {
af.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var $g.cur_mod.name $g.is_builtin_mod')
}
}
g.autofree_scope_stmts << af.str()
}
@ -3697,10 +3702,8 @@ fn (mut g Gen) ident(node ast.Ident) {
g.write('${name}.val')
return
}
// TODO: investigate why node.obj is pointing to outdated ScopeObject?
// v := node.obj
// if v is ast.Var {
if v := node.scope.find_var(node.name) {
v := node.obj
if v is ast.Var {
is_auto_heap = v.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign)
if is_auto_heap {
g.write('(*(')

View File

@ -1206,6 +1206,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// `json__encode` => `json__encode_User`
// encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
encode_name := js_enc_name(json_type_str)
g.empty_line = true
g.writeln('// json.encode')
g.write('cJSON* $json_obj = ${encode_name}(')
if node.args[0].typ.is_ptr() {
@ -1225,6 +1226,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
typ := c_name(g.typ(ast_type.typ))
fn_name := c_name(name) + '_' + typ
g.gen_json_for_type(ast_type.typ)
g.empty_line = true
g.writeln('// json.decode')
g.write('cJSON* $json_obj = json__json_parse(')
// Skip the first argument in json.decode which is a type
@ -1233,10 +1235,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
g.call_args(node)
g.writeln(');')
tmp2 = g.new_tmp_var()
g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);')
g.writeln('Option_$typ $tmp2 = ${fn_name}($json_obj);')
}
if !g.is_autofree {
g.write('cJSON_Delete($json_obj); //del')
g.write('cJSON_Delete($json_obj); // del')
}
g.write('\n$cur_line')
name = ''

View File

@ -626,7 +626,8 @@ fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) {
if elem_sym.kind == .function {
g.write(', _MOV((voidptr[]){ ')
} else if elem_is_array_var {
g.write(', &')
addr := if elem_sym.kind == .array_fixed { '' } else { '&' }
g.write(', $addr')
} else {
g.write(', _MOV(($elem_type_str[]){ ')
}

View File

@ -245,10 +245,6 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
println('$path:${i + 1}: $line')
}
}
mut scope := &ast.Scope{
start_pos: 0
parent: p.table.global_scope
}
$if trace_comptime ? {
println('')
println('>>> template for $path:')
@ -256,25 +252,11 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
println('>>> end of template END')
println('')
}
mut file := parse_comptime(v_code, p.table, p.pref, scope)
// the tmpl inherits all parent scopes. previous functionality was just to
// inherit the scope from which the comptime call was made and no parents.
// this is much simpler and allws access to globals. can be changed if needed.
mut file := parse_comptime(tmpl_path, v_code, p.table, p.pref, p.scope)
file.path = tmpl_path
// copy vars from current fn scope into vweb_tmpl scope
for mut stmt in file.stmts {
if mut stmt is ast.FnDecl {
if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' {
for _, mut obj in p.scope.objects {
if mut obj is ast.Var {
stmt.scope.register(ast.Var{
...obj
is_used: true
pos: stmt.body_pos
})
}
}
break
}
}
}
return ast.ComptimeCall{
scope: 0
is_vweb: true

View File

@ -115,11 +115,12 @@ pub fn parse_stmt(text string, table &ast.Table, scope &ast.Scope) ast.Stmt {
return p.stmt(false)
}
pub fn parse_comptime(text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File {
pub fn parse_comptime(tmpl_path string, text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File {
$if trace_parse_comptime ? {
eprintln('> ${@MOD}.${@FN} text: $text')
}
mut p := Parser{
file_name: tmpl_path
scanner: scanner.new_scanner(text, .skip_comments, pref)
table: table
pref: pref
@ -2139,10 +2140,12 @@ pub fn (mut p Parser) name_expr() ast.Expr {
p.expr_mod = ''
// `map[string]int` initialization
if p.tok.lit == 'map' && p.peek_tok.kind == .lsbr {
mut pos := p.tok.pos()
map_type := p.parse_map_type()
if p.tok.kind == .lcbr {
p.next()
if p.tok.kind == .rcbr {
pos = pos.extend(p.tok.pos())
p.next()
} else {
if p.pref.is_fmt {
@ -2155,7 +2158,7 @@ pub fn (mut p Parser) name_expr() ast.Expr {
}
return ast.MapInit{
typ: map_type
pos: p.prev_tok.pos()
pos: pos
}
}
// `chan typ{...}`

View File

@ -184,7 +184,8 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
p.next()
is_field_volatile = true
}
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital())
is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()
&& (p.peek_tok.kind != .lsbr || p.peek_token(2).kind != .rsbr))
|| p.peek_tok.kind == .dot) && language == .v && p.peek_tok.kind != .key_fn
is_on_top := ast_fields.len == 0 && !(is_field_mut || is_field_global)
mut field_name := ''

View File

@ -110,7 +110,7 @@ pub mut:
test_runner string // can be 'simple' (fastest, but much less detailed), 'tap', 'normal'
profile_file string // the profile results will be stored inside profile_file
profile_no_inline bool // when true, [inline] functions would not be profiled
profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on.
profile_fns []string // when set, profiling will be off by default, but inside these functions (and what they call) it will be on.
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
is_prod bool // use "-O2"
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
@ -634,7 +634,7 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
eprintln('Use `v $arg` instead.')
exit(1)
}
if arg[0] == `-` {
if arg.len != 0 && arg[0] == `-` {
if arg[1..] in pref.list_of_flags_with_param {
// skip parameter
i++

View File

@ -0,0 +1,8 @@
module pref
fn test_check_parametes() {
// reproducing issue https://github.com/vlang/v/issues/13983
_, cmd := parse_args_and_show_errors(['help'], [''], true)
// no command found from args
assert cmd == ''
}

View File

@ -30,3 +30,12 @@ fn test_array_eval_count() {
a4 = Counter{}
assert a4.new_arr('all() failed').all(it == 2) == false
}
fn opt_bool_fn() ?bool {
return true
}
fn test_any_called_with_opt_bool_fn() ? {
_ := [1, 2, 3].any(opt_bool_fn() ?)
assert true
}

View File

@ -0,0 +1,2 @@
$a.name
$b

View File

@ -0,0 +1,13 @@
[heap]
struct MyHeapStruct {
name string
}
// make sure dereferencing of heap stucts works in selector expr (in tmpl),
fn test_heap_struct_dereferencing_in_selector_expr() {
a := MyHeapStruct{
name: 'my_heap_struct_a'
}
b := 2
$tmpl('comptime_call_tmpl_variable_scope_test.tpl')
}

View File

@ -0,0 +1,44 @@
struct Test1 {
mut:
value [4]int
}
fn (mut t Test1) set(new_value [4]int) {
t.value = new_value
}
fn test_fn_with_fixed_array_argument_1() {
mut t := Test1{}
println(t)
assert '$t.value' == '[0, 0, 0, 0]'
t.set([1, 2, 3, 4]!)
println(t)
assert '$t.value' == '[1, 2, 3, 4]'
}
struct Test2 {
mut:
fixed_value [2][4]int
dynamic_value [][4]int
}
fn (mut t Test2) set(index int, new_value [4]int) {
t.fixed_value[index] = new_value
t.dynamic_value << new_value
}
fn test_fn_with_fixed_array_argument_2() {
mut t := Test2{}
println(t)
assert '$t.fixed_value' == '[[0, 0, 0, 0], [0, 0, 0, 0]]'
assert '$t.dynamic_value' == '[]'
t.set(0, [1, 2, 3, 4]!)
println(t)
assert '$t.fixed_value' == '[[1, 2, 3, 4], [0, 0, 0, 0]]'
assert '$t.dynamic_value' == '[[1, 2, 3, 4]]'
}

View File

@ -16,8 +16,6 @@ fn create(x int) &Foo {
fn (f &Foo) free() {
println('> freeing Foo $f.x at address: ${voidptr(f)} | frees.len: $frees.len')
frees << f.x
// TODO: this should NOT be necessary - the compiler should do it automatically in the parent scope
unsafe { free(f) }
}
fn create_some_foos() {