Compare commits
24 Commits
3baf1741ba
...
41857b0acf
| Author | SHA1 | Date |
|---|---|---|
|
|
41857b0acf | |
|
|
95d94e683e | |
|
|
b2b82931d1 | |
|
|
010ace62e1 | |
|
|
6c4f1ce196 | |
|
|
4b48bece20 | |
|
|
ee6d819f22 | |
|
|
0badffe651 | |
|
|
f9c4cbdb15 | |
|
|
0669e5952b | |
|
|
df0133c89b | |
|
|
eb9063bdb7 | |
|
|
e76e0a97e1 | |
|
|
bf8d1f415d | |
|
|
cd5c9a83c5 | |
|
|
bf10b27a33 | |
|
|
633e612d62 | |
|
|
93d49176c7 | |
|
|
374b6927bc | |
|
|
21555fb03a | |
|
|
6468bcd0a6 | |
|
|
9a1d73f622 | |
|
|
8d1cc6a817 | |
|
|
bf86fff9cc |
|
|
@ -56,11 +56,14 @@ to the AST.
|
|||
7. `v/gen/c` C backend. It simply walks the AST and generates C code that can be
|
||||
compiled with Clang, GCC, Visual Studio, and TCC.
|
||||
|
||||
8. `json.v` defines the json code generation. This file will be removed once V
|
||||
8. `v/gen/js` JavaScript backend. It simply walks the AST and generates JS code that can be
|
||||
executed on the browser or in NodeJS/Deno.
|
||||
|
||||
9. `v/gen/c/json.v` defines the json code generation. This file will be removed once V
|
||||
supports comptime code generation, and it will be possible to do this using the
|
||||
language's tools.
|
||||
|
||||
9. `v/gen/native` is the directory with all the machine code generation logic. It
|
||||
10. `v/gen/native` is the directory with all the machine code generation logic. It
|
||||
defines a set of functions that translate assembly instructions to machine code
|
||||
and build the binary from scratch byte by byte. It manually builds all headers,
|
||||
segments, sections, symtable, relocations, etc. Right now it only has basic
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ fn html_highlight(code string, tb &ast.Table) string {
|
|||
} else if typ == .char {
|
||||
'`$tok.lit`'
|
||||
} else if typ == .comment {
|
||||
if tok.lit[0] == 1 { '//${tok.lit[1..]}' } else { '//$tok.lit' }
|
||||
if tok.lit != '' && tok.lit[0] == 1 { '//${tok.lit[1..]}' } else { '//$tok.lit' }
|
||||
} else {
|
||||
tok.lit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
module main
|
||||
|
||||
const (
|
||||
source_root = 'temp'
|
||||
source_root = 'temp' // some const
|
||||
another = int(5) //
|
||||
)
|
||||
const (
|
||||
windowpos_undefined_mask = C.SDL_WINDOWPOS_UNDEFINED_MASK // 0x1FFF0000u
|
||||
windowpos_undefined = C.SDL_WINDOWPOS_UNDEFINED //
|
||||
)
|
||||
Used to indicate that you don't care what the window position is.
|
||||
fn funky()
|
||||
funky - comment for function below
|
||||
funky - comment for function below
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
module main
|
||||
|
||||
const (
|
||||
source_root = 'temp'
|
||||
source_root = 'temp' // some const
|
||||
another = int(5) //
|
||||
)
|
||||
fn funky()
|
||||
const (
|
||||
windowpos_undefined_mask = C.SDL_WINDOWPOS_UNDEFINED_MASK // 0x1FFF0000u
|
||||
windowpos_undefined = C.SDL_WINDOWPOS_UNDEFINED //
|
||||
)
|
||||
fn funky()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
pub const (
|
||||
source_root = 'temp'
|
||||
source_root = 'temp' // some const
|
||||
another = int(5) //
|
||||
)
|
||||
|
||||
// Used to indicate that you don't care what the window position is.
|
||||
pub const (
|
||||
windowpos_undefined_mask = C.SDL_WINDOWPOS_UNDEFINED_MASK // 0x1FFF0000u
|
||||
windowpos_undefined = C.SDL_WINDOWPOS_UNDEFINED //
|
||||
)
|
||||
|
||||
// funky - comment for function below
|
||||
|
|
|
|||
|
|
@ -162,7 +162,11 @@ fn color_highlight(code string, tb &ast.Table) string {
|
|||
lit = term.yellow('`$tok.lit`')
|
||||
}
|
||||
.comment {
|
||||
lit = if tok.lit[0] == 1 { '//${tok.lit[1..]}' } else { '//$tok.lit' }
|
||||
lit = if tok.lit != '' && tok.lit[0] == 1 {
|
||||
'//${tok.lit[1..]}'
|
||||
} else {
|
||||
'//$tok.lit'
|
||||
}
|
||||
}
|
||||
.keyword {
|
||||
lit = term.bright_blue(tok.lit)
|
||||
|
|
@ -209,16 +213,18 @@ fn color_highlight(code string, tb &ast.Table) string {
|
|||
} else if
|
||||
next_tok.kind in [.lcbr, .rpar, .eof, .comma, .pipe, .name, .rcbr, .assign, .key_pub, .key_mut, .pipe, .comma]
|
||||
&& prev.kind in [.name, .amp, .rsbr, .key_type, .assign, .dot, .question, .rpar, .key_struct, .key_enum, .pipe, .key_interface]
|
||||
&& (tok.lit[0].is_capital() || prev_prev.lit in ['C', 'JS']) {
|
||||
&& ((tok.lit != '' && tok.lit[0].is_capital())
|
||||
|| prev_prev.lit in ['C', 'JS']) {
|
||||
tok_typ = .symbol
|
||||
} else if next_tok.kind == .lpar || (!tok.lit[0].is_capital()
|
||||
&& next_tok.kind == .lt && next_tok.pos == tok.pos + tok.lit.len) {
|
||||
} else if next_tok.kind == .lpar
|
||||
|| (!(tok.lit != '' && tok.lit[0].is_capital()) && next_tok.kind == .lt
|
||||
&& next_tok.pos == tok.pos + tok.lit.len) {
|
||||
tok_typ = .function
|
||||
} else if next_tok.kind == .dot {
|
||||
if tok.lit in ['C', 'JS'] {
|
||||
tok_typ = .prefix
|
||||
} else {
|
||||
if tok.lit[0].is_capital() {
|
||||
if tok.lit != '' && tok.lit[0].is_capital() {
|
||||
tok_typ = .symbol
|
||||
} else {
|
||||
tok_typ = .module_
|
||||
|
|
|
|||
|
|
@ -1557,3 +1557,15 @@ fn test_generic_mutable_arrays() {
|
|||
mut arr := [1, 2, 3]
|
||||
assert example(mut arr) == [1, 2, 3]
|
||||
}
|
||||
|
||||
struct Ok {}
|
||||
|
||||
fn test_inline_array_element_access() {
|
||||
println([Ok{}][0])
|
||||
a1 := [Ok{}][0]
|
||||
assert a1 == Ok{}
|
||||
|
||||
println([1][0])
|
||||
a2 := [1][0]
|
||||
assert a2 == 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,12 +64,20 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
|
|||
vhalt()
|
||||
}
|
||||
|
||||
// panic_optional_not_set prints given optional not set and exits the process
|
||||
// panic_optional_not_set is called by V, when you use option error propagation in your main function.
|
||||
// It ends the program with a panic.
|
||||
[noreturn]
|
||||
pub fn panic_optional_not_set(s string) {
|
||||
panic('optional not set ($s)')
|
||||
}
|
||||
|
||||
// panic_optional_not_set is called by V, when you use result error propagation in your main function
|
||||
// It ends the program with a panic.
|
||||
[noreturn]
|
||||
pub fn panic_result_not_set(s string) {
|
||||
panic('result not set ($s)')
|
||||
}
|
||||
|
||||
// panic prints a nice error message, then exits the process with exit code of 1.
|
||||
// It also shows a backtrace on most platforms.
|
||||
[noreturn]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
module builtin
|
||||
|
||||
// These are just dummy implementations to appease linking on musl/alpine
|
||||
|
||||
[export: 'backtrace_symbols']
|
||||
[weak]
|
||||
fn vbacktrace_symbols(const_buffer &voidptr, size int) &&char {
|
||||
return 0
|
||||
}
|
||||
|
||||
[export: 'backtrace']
|
||||
[weak]
|
||||
fn vbacktrace(buffer &voidptr, size int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
[export: 'backtrace_symbols_fd']
|
||||
[weak]
|
||||
fn vbacktrace_symbols_fd(const_buffer &voidptr, size int, fd int) {
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
module builtin
|
||||
|
||||
// <execinfo.h>
|
||||
fn C.backtrace(a &voidptr, size int) int
|
||||
fn C.backtrace_symbols(a &voidptr, size int) &&char
|
||||
fn C.backtrace_symbols_fd(a &voidptr, size int, fd int)
|
||||
|
|
@ -45,10 +45,13 @@ $if dynamic_boehm ? {
|
|||
#flag -DBUS_PAGE_FAULT=T_PAGEFLT
|
||||
#flag -DGC_PTHREADS=1
|
||||
$if !tinyc {
|
||||
#flag -I@VEXEROOT/thirdparty/libgc/include
|
||||
#flag @VEXEROOT/thirdparty/libgc/gc.o
|
||||
} $else {
|
||||
}
|
||||
$if tinyc {
|
||||
#flag -I/usr/local/include
|
||||
#flag $first_existing("/usr/local/lib/libgc.a", "/usr/lib/libgc.a")
|
||||
#flag -lgc
|
||||
}
|
||||
#flag -lpthread
|
||||
} $else $if openbsd {
|
||||
|
|
|
|||
|
|
@ -39,13 +39,6 @@ fn C.isdigit(c int) bool
|
|||
// stdio.h
|
||||
fn C.popen(c &char, t &char) voidptr
|
||||
|
||||
// <execinfo.h>
|
||||
fn C.backtrace(a &voidptr, size int) int
|
||||
|
||||
fn C.backtrace_symbols(a &voidptr, size int) &&char
|
||||
|
||||
fn C.backtrace_symbols_fd(a &voidptr, size int, fd int)
|
||||
|
||||
// <libproc.h>
|
||||
pub fn proc_pidpath(int, voidptr, int) int
|
||||
|
||||
|
|
@ -100,6 +93,8 @@ fn C._execve(cmd_path &char, args voidptr, envs voidptr) int
|
|||
|
||||
fn C._execvp(cmd_path &char, args &&char) int
|
||||
|
||||
fn C.strcmp(s1 &char, s2 &char) int
|
||||
|
||||
[trusted]
|
||||
fn C.fork() int
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ pub:
|
|||
pos token.Pos
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct StringLiteral {
|
||||
pub:
|
||||
val string
|
||||
|
|
@ -246,6 +247,7 @@ pub enum GenericKindField {
|
|||
}
|
||||
|
||||
// `foo.bar`
|
||||
[minify]
|
||||
pub struct SelectorExpr {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -287,11 +289,13 @@ pub:
|
|||
is_skipped bool // module main can be skipped in single file programs
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct StructField {
|
||||
pub:
|
||||
pos token.Pos
|
||||
type_pos token.Pos
|
||||
comments []Comment
|
||||
i int
|
||||
has_default_expr bool
|
||||
attrs []Attr
|
||||
is_pub bool
|
||||
|
|
@ -333,6 +337,7 @@ pub mut:
|
|||
}
|
||||
|
||||
// const declaration
|
||||
[minify]
|
||||
pub struct ConstDecl {
|
||||
pub:
|
||||
is_pub bool
|
||||
|
|
@ -344,6 +349,7 @@ pub mut:
|
|||
is_block bool // const() block
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct StructDecl {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -380,6 +386,7 @@ pub:
|
|||
comments []Comment
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct InterfaceDecl {
|
||||
pub:
|
||||
name string
|
||||
|
|
@ -431,6 +438,7 @@ pub mut:
|
|||
// ...a
|
||||
// field1: 'hello'
|
||||
// }`
|
||||
[minify]
|
||||
pub struct StructInit {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -484,6 +492,7 @@ pub mut:
|
|||
}
|
||||
|
||||
// function or method declaration
|
||||
[minify]
|
||||
pub struct FnDecl {
|
||||
pub:
|
||||
name string // 'math.bits.normalize'
|
||||
|
|
@ -542,6 +551,7 @@ pub mut:
|
|||
}
|
||||
|
||||
// break, continue
|
||||
[minify]
|
||||
pub struct BranchStmt {
|
||||
pub:
|
||||
kind token.Kind
|
||||
|
|
@ -550,6 +560,7 @@ pub:
|
|||
}
|
||||
|
||||
// function or method call expr
|
||||
[minify]
|
||||
pub struct CallExpr {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -589,6 +600,7 @@ pub struct AutofreeArgVar {
|
|||
}
|
||||
*/
|
||||
// function call argument: `f(callarg)`
|
||||
[minify]
|
||||
pub struct CallArg {
|
||||
pub:
|
||||
is_mut bool
|
||||
|
|
@ -612,6 +624,7 @@ pub mut:
|
|||
types []Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Var {
|
||||
pub:
|
||||
name string
|
||||
|
|
@ -643,6 +656,7 @@ pub mut:
|
|||
|
||||
// used for smartcasting only
|
||||
// struct fields change type in scopes
|
||||
[minify]
|
||||
pub struct ScopeStructField {
|
||||
pub:
|
||||
struct_type Type // type of struct
|
||||
|
|
@ -657,6 +671,7 @@ pub:
|
|||
// 12 <- the current casted type (typ)
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct GlobalField {
|
||||
pub:
|
||||
name string
|
||||
|
|
@ -682,6 +697,7 @@ pub mut:
|
|||
end_comments []Comment
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct EmbeddedFile {
|
||||
pub:
|
||||
rpath string // used in the source code, as an ID/key to the embed
|
||||
|
|
@ -749,6 +765,7 @@ pub mut:
|
|||
|
||||
// TODO: (joe) remove completely, use ident.obj
|
||||
// instead which points to the scope object
|
||||
[minify]
|
||||
pub struct IdentVar {
|
||||
pub mut:
|
||||
typ Type
|
||||
|
|
@ -771,6 +788,7 @@ pub enum IdentKind {
|
|||
}
|
||||
|
||||
// A single identifier
|
||||
[minify]
|
||||
pub struct Ident {
|
||||
pub:
|
||||
language Language
|
||||
|
|
@ -816,6 +834,7 @@ pub fn (i &Ident) var_info() IdentVar {
|
|||
|
||||
// left op right
|
||||
// See: token.Kind.is_infix
|
||||
[minify]
|
||||
pub struct InfixExpr {
|
||||
pub:
|
||||
op token.Kind
|
||||
|
|
@ -846,6 +865,7 @@ pub mut:
|
|||
}
|
||||
|
||||
// See: token.Kind.is_prefix
|
||||
[minify]
|
||||
pub struct PrefixExpr {
|
||||
pub:
|
||||
op token.Kind
|
||||
|
|
@ -857,6 +877,7 @@ pub mut:
|
|||
is_option bool // IfGuard
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct IndexExpr {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -874,6 +895,7 @@ pub mut:
|
|||
is_gated bool // #[] gated array
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct IfExpr {
|
||||
pub:
|
||||
is_comptime bool
|
||||
|
|
@ -921,6 +943,7 @@ pub mut:
|
|||
scope &Scope
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct MatchExpr {
|
||||
pub:
|
||||
tok_kind token.Kind
|
||||
|
|
@ -959,6 +982,7 @@ pub mut:
|
|||
expected_type Type // for debugging only
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct SelectBranch {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -1000,6 +1024,7 @@ pub mut:
|
|||
scope &Scope
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct ForInStmt {
|
||||
pub:
|
||||
key_var string
|
||||
|
|
@ -1060,6 +1085,7 @@ pub:
|
|||
}
|
||||
*/
|
||||
// variable assign statement
|
||||
[minify]
|
||||
pub struct AssignStmt {
|
||||
pub:
|
||||
op token.Kind // include: =,:=,+=,-=,*=,/= and so on; for a list of all the assign operators, see vlib/token/token.v
|
||||
|
|
@ -1111,6 +1137,7 @@ pub mut:
|
|||
}
|
||||
|
||||
// enum declaration
|
||||
[minify]
|
||||
pub struct EnumDecl {
|
||||
pub:
|
||||
name string
|
||||
|
|
@ -1161,6 +1188,7 @@ pub:
|
|||
// TODO: handle this differently
|
||||
// v1 excludes non current os ifdefs so
|
||||
// the defer's never get added in the first place
|
||||
[minify]
|
||||
pub struct DeferStmt {
|
||||
pub:
|
||||
stmts []Stmt
|
||||
|
|
@ -1179,6 +1207,7 @@ pub mut:
|
|||
expr Expr
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct GoExpr {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -1199,6 +1228,7 @@ pub:
|
|||
pos token.Pos
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct ArrayInit {
|
||||
pub:
|
||||
pos token.Pos // `[]` in []Type{} position
|
||||
|
|
@ -1242,6 +1272,7 @@ pub mut:
|
|||
elem_type Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct MapInit {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -1257,6 +1288,7 @@ pub mut:
|
|||
}
|
||||
|
||||
// s[10..20]
|
||||
[minify]
|
||||
pub struct RangeExpr {
|
||||
pub:
|
||||
has_high bool
|
||||
|
|
@ -1268,6 +1300,7 @@ pub mut:
|
|||
high Expr
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct CastExpr {
|
||||
pub mut:
|
||||
arg Expr // `n` in `string(buf, n)`
|
||||
|
|
@ -1279,6 +1312,7 @@ pub mut:
|
|||
pos token.Pos
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct AsmStmt {
|
||||
pub:
|
||||
arch pref.Arch
|
||||
|
|
@ -1296,6 +1330,7 @@ pub mut:
|
|||
local_labels []string // local to the assembly block
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct AsmTemplate {
|
||||
pub mut:
|
||||
name string
|
||||
|
|
@ -1460,6 +1495,7 @@ pub const (
|
|||
}
|
||||
)
|
||||
|
||||
[minify]
|
||||
pub struct AssertStmt {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -1511,6 +1547,7 @@ pub:
|
|||
*/
|
||||
|
||||
// deprecated
|
||||
[minify]
|
||||
pub struct Assoc {
|
||||
pub:
|
||||
var_name string
|
||||
|
|
@ -1540,6 +1577,7 @@ pub mut:
|
|||
typ Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct OffsetOf {
|
||||
pub:
|
||||
struct_type Type
|
||||
|
|
@ -1555,6 +1593,7 @@ pub mut:
|
|||
expr Expr
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct TypeOf {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -1563,6 +1602,7 @@ pub mut:
|
|||
expr_type Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct DumpExpr {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
@ -1598,6 +1638,7 @@ pub mut:
|
|||
val string
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct ComptimeSelector {
|
||||
pub:
|
||||
has_parens bool // if $() is used, for vfmt
|
||||
|
|
@ -1609,6 +1650,7 @@ pub mut:
|
|||
typ Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct ComptimeCall {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pub enum AttrKind {
|
|||
}
|
||||
|
||||
// e.g. `[unsafe]`
|
||||
[minify]
|
||||
pub struct Attr {
|
||||
pub:
|
||||
name string // [name]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import v.cflag
|
|||
import v.token
|
||||
import v.util
|
||||
|
||||
[heap]
|
||||
[heap; minify]
|
||||
pub struct Table {
|
||||
mut:
|
||||
parsing_type string // name of the type to enable recursive type parsing
|
||||
|
|
@ -86,6 +86,7 @@ pub fn (t &Table) panic(message string) {
|
|||
t.panic_handler(t, message)
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Fn {
|
||||
pub:
|
||||
is_variadic bool
|
||||
|
|
@ -126,6 +127,7 @@ fn (f &Fn) method_equals(o &Fn) bool {
|
|||
&& f.name == o.name
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Param {
|
||||
pub:
|
||||
pos token.Pos
|
||||
|
|
|
|||
|
|
@ -41,14 +41,22 @@ fn test_type_size() ? {
|
|||
|
||||
mut t := b.table
|
||||
|
||||
assert sizeof(T01) == t.type_size(t.type_idxs['main.T01'] ?)
|
||||
assert sizeof(T02) == t.type_size(t.type_idxs['main.T02'] ?)
|
||||
assert sizeof(T03) == t.type_size(t.type_idxs['main.T03'] ?)
|
||||
assert sizeof(T04) == t.type_size(t.type_idxs['main.T04'] ?)
|
||||
assert sizeof(T05) == t.type_size(t.type_idxs['main.T05'] ?)
|
||||
assert sizeof(T06) == t.type_size(t.type_idxs['main.T06'] ?)
|
||||
assert sizeof(T07) == t.type_size(t.type_idxs['main.T07'] ?)
|
||||
assert sizeof(T08) == t.type_size(t.type_idxs['main.T08'] ?)
|
||||
size01, _ := t.type_size(t.type_idxs['main.T01'] ?)
|
||||
assert sizeof(T01) == size01
|
||||
size02, _ := t.type_size(t.type_idxs['main.T02'] ?)
|
||||
assert sizeof(T02) == size02
|
||||
size03, _ := t.type_size(t.type_idxs['main.T03'] ?)
|
||||
assert sizeof(T03) == size03
|
||||
size04, _ := t.type_size(t.type_idxs['main.T04'] ?)
|
||||
assert sizeof(T04) == size04
|
||||
size05, _ := t.type_size(t.type_idxs['main.T05'] ?)
|
||||
assert sizeof(T05) == size05
|
||||
size06, _ := t.type_size(t.type_idxs['main.T06'] ?)
|
||||
assert sizeof(T06) == size06
|
||||
size07, _ := t.type_size(t.type_idxs['main.T07'] ?)
|
||||
assert sizeof(T07) == size07
|
||||
size08, _ := t.type_size(t.type_idxs['main.T08'] ?)
|
||||
assert sizeof(T08) == size08
|
||||
|
||||
println('done')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
|
|||
// Each TypeSymbol is entered into `Table.types`.
|
||||
// See also: Table.sym.
|
||||
|
||||
[minify]
|
||||
pub struct TypeSymbol {
|
||||
pub:
|
||||
parent_idx int
|
||||
|
|
@ -93,6 +94,8 @@ pub mut:
|
|||
is_pub bool
|
||||
language Language
|
||||
idx int
|
||||
size int = -1
|
||||
align int = -1
|
||||
}
|
||||
|
||||
// max of 8
|
||||
|
|
@ -825,88 +828,100 @@ pub fn (t &TypeSymbol) is_builtin() bool {
|
|||
return t.mod == 'builtin'
|
||||
}
|
||||
|
||||
// type_size returns the size in bytes of `typ`, similarly to C's `sizeof()`.
|
||||
pub fn (t &Table) type_size(typ Type) int {
|
||||
// type_size returns the size and alignment (in bytes) of `typ`, similarly to C's `sizeof()` and `alignof()`.
|
||||
pub fn (t &Table) type_size(typ Type) (int, int) {
|
||||
if typ.has_flag(.optional) {
|
||||
return t.type_size(ast.error_type_idx)
|
||||
}
|
||||
if typ.nr_muls() > 0 {
|
||||
return t.pointer_size
|
||||
return t.pointer_size, t.pointer_size
|
||||
}
|
||||
sym := t.sym(typ)
|
||||
mut sym := t.sym(typ)
|
||||
if sym.size != -1 {
|
||||
return sym.size, sym.align
|
||||
}
|
||||
mut size := 0
|
||||
mut align := 0
|
||||
match sym.kind {
|
||||
.placeholder, .void, .none_ {
|
||||
return 0
|
||||
}
|
||||
.placeholder, .void, .none_, .generic_inst {}
|
||||
.voidptr, .byteptr, .charptr, .function, .usize, .isize, .any, .thread, .chan {
|
||||
return t.pointer_size
|
||||
size = t.pointer_size
|
||||
}
|
||||
.i8, .u8, .char, .bool {
|
||||
return 1
|
||||
size = 1
|
||||
align = 1
|
||||
}
|
||||
.i16, .u16 {
|
||||
return 2
|
||||
size = 2
|
||||
align = 2
|
||||
}
|
||||
.int, .u32, .rune, .f32, .enum_ {
|
||||
return 4
|
||||
size = 4
|
||||
align = 4
|
||||
}
|
||||
.i64, .u64, .int_literal, .f64, .float_literal {
|
||||
return 8
|
||||
size = 8
|
||||
align = 8
|
||||
}
|
||||
.alias {
|
||||
return t.type_size((sym.info as Alias).parent_type)
|
||||
size, align = t.type_size((sym.info as Alias).parent_type)
|
||||
}
|
||||
.struct_, .string, .multi_return {
|
||||
mut max_alignment := 0
|
||||
mut total_size := 0
|
||||
types := if sym.info is Struct {
|
||||
types := if mut sym.info is Struct {
|
||||
sym.info.fields.map(it.typ)
|
||||
} else {
|
||||
(sym.info as MultiReturn).types
|
||||
}
|
||||
for ftyp in types {
|
||||
field_size := t.type_size(ftyp)
|
||||
alignment := if field_size > t.pointer_size { t.pointer_size } else { field_size }
|
||||
field_size, alignment := t.type_size(ftyp)
|
||||
if alignment > max_alignment {
|
||||
max_alignment = alignment
|
||||
}
|
||||
total_size = round_up(total_size, alignment) + field_size
|
||||
}
|
||||
return round_up(total_size, max_alignment)
|
||||
size = round_up(total_size, max_alignment)
|
||||
align = max_alignment
|
||||
}
|
||||
.sum_type, .interface_, .aggregate {
|
||||
match sym.info {
|
||||
match mut sym.info {
|
||||
SumType, Aggregate {
|
||||
return (sym.info.fields.len + 2) * t.pointer_size
|
||||
size = (sym.info.fields.len + 2) * t.pointer_size
|
||||
align = t.pointer_size
|
||||
}
|
||||
Interface {
|
||||
mut res := (sym.info.fields.len + 2) * t.pointer_size
|
||||
size = (sym.info.fields.len + 2) * t.pointer_size
|
||||
align = t.pointer_size
|
||||
for etyp in sym.info.embeds {
|
||||
res += t.type_size(etyp) - 2 * t.pointer_size
|
||||
esize, _ := t.type_size(etyp)
|
||||
size += esize - 2 * t.pointer_size
|
||||
}
|
||||
return res
|
||||
}
|
||||
else {
|
||||
// unreachable
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
.array_fixed {
|
||||
info := sym.info as ArrayFixed
|
||||
return info.size * t.type_size(info.elem_type)
|
||||
elem_size, elem_align := t.type_size(info.elem_type)
|
||||
size = info.size * elem_size
|
||||
align = elem_align
|
||||
}
|
||||
// TODO hardcoded:
|
||||
.map {
|
||||
return if t.pointer_size == 8 { 120 } else { 80 }
|
||||
size = if t.pointer_size == 8 { 120 } else { 80 }
|
||||
align = t.pointer_size
|
||||
}
|
||||
.array {
|
||||
return if t.pointer_size == 8 { 32 } else { 24 }
|
||||
}
|
||||
.generic_inst {
|
||||
return 0
|
||||
size = if t.pointer_size == 8 { 32 } else { 24 }
|
||||
align = t.pointer_size
|
||||
}
|
||||
}
|
||||
sym.size = size
|
||||
sym.align = align
|
||||
return size, align
|
||||
}
|
||||
|
||||
// round_up rounds the number `n` up to the next multiple `multiple`.
|
||||
|
|
@ -972,6 +987,7 @@ pub fn (kinds []Kind) str() string {
|
|||
return kinds_str
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Struct {
|
||||
pub:
|
||||
attrs []Attr
|
||||
|
|
@ -981,6 +997,7 @@ pub mut:
|
|||
is_typedef bool // C. [typedef]
|
||||
is_union bool
|
||||
is_heap bool
|
||||
is_minify bool
|
||||
is_generic bool
|
||||
generic_types []Type
|
||||
concrete_types []Type
|
||||
|
|
@ -994,6 +1011,7 @@ pub mut:
|
|||
concrete_types []Type // concrete types, e.g. <int, string>
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Interface {
|
||||
pub mut:
|
||||
types []Type // all types that implement this interface
|
||||
|
|
@ -1014,8 +1032,10 @@ pub:
|
|||
vals []string
|
||||
is_flag bool
|
||||
is_multi_allowed bool
|
||||
uses_exprs bool
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Alias {
|
||||
pub:
|
||||
parent_type Type
|
||||
|
|
@ -1038,6 +1058,7 @@ pub mut:
|
|||
elem_type Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct ArrayFixed {
|
||||
pub:
|
||||
size int
|
||||
|
|
@ -1063,6 +1084,7 @@ pub mut:
|
|||
value_type Type
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct SumType {
|
||||
pub mut:
|
||||
fields []StructField
|
||||
|
|
@ -1309,6 +1331,7 @@ fn (t Table) shorten_user_defined_typenames(originalname string, import_aliases
|
|||
return res
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct FnSignatureOpts {
|
||||
skip_receiver bool
|
||||
type_only bool
|
||||
|
|
|
|||
|
|
@ -661,10 +661,6 @@ pub fn (mut v Builder) cc() {
|
|||
break
|
||||
}
|
||||
if v.pref.compress {
|
||||
$if windows {
|
||||
println('-compress does not work on Windows for now')
|
||||
return
|
||||
}
|
||||
ret := os.system('strip $v.pref.out_name')
|
||||
if ret != 0 {
|
||||
println('strip failed')
|
||||
|
|
@ -685,7 +681,7 @@ pub fn (mut v Builder) cc() {
|
|||
println('install upx\n' + 'for example, on Debian/Ubuntu run `sudo apt install upx`')
|
||||
}
|
||||
$if windows {
|
||||
// :)
|
||||
println('install upx')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// allow rune -> any int and vice versa
|
||||
if (expected == ast.rune_type && got.is_int())
|
||||
|| (got == ast.rune_type && expected.is_int()) {
|
||||
return true
|
||||
}
|
||||
got_sym := c.table.sym(got)
|
||||
expected_sym := c.table.sym(expected)
|
||||
if got_sym.kind == .enum_ {
|
||||
|
|
@ -43,6 +49,18 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||
// Allow fixed arrays as `&i8` etc
|
||||
if expected_sym.is_number() {
|
||||
return true
|
||||
} else if expected.is_any_kind_of_pointer() {
|
||||
return true
|
||||
}
|
||||
} else if expected_sym.kind == .array_fixed {
|
||||
if got_sym.is_number() && got.is_any_kind_of_pointer() {
|
||||
return true
|
||||
} else if got_sym.kind == .array {
|
||||
info := expected_sym.info as ast.ArrayFixed
|
||||
info2 := got_sym.info as ast.Array
|
||||
if c.check_types(info.elem_type, info2.elem_type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if expected_sym.kind == .enum_ && got_sym.is_number() {
|
||||
|
|
@ -187,30 +205,35 @@ pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type,
|
|||
return
|
||||
}
|
||||
}
|
||||
got_typ_sym := c.table.sym(got)
|
||||
got_typ_str := c.table.type_to_str(got.clear_flag(.variadic))
|
||||
expected_typ_sym := c.table.sym(expected_)
|
||||
expected_typ_str := c.table.type_to_str(expected.clear_flag(.variadic))
|
||||
|
||||
if c.check_types(got, expected) {
|
||||
if language != .v || expected.is_ptr() == got.is_ptr() || arg.is_mut
|
||||
|| arg.expr.is_auto_deref_var() || got.has_flag(.shared_f)
|
||||
|| c.table.sym(expected_).kind !in [.array, .map] {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check on Generics types, there are some case where we have the following case
|
||||
// `&Type<int> == &Type<>`. This is a common case we are implementing a function
|
||||
// with generic parameters like `compare(bst Bst<T> node) {}`
|
||||
got_typ_sym := c.table.sym(got)
|
||||
got_typ_str := c.table.type_to_str(got.clear_flag(.variadic))
|
||||
expected_typ_sym := c.table.sym(expected_)
|
||||
expected_typ_str := c.table.type_to_str(expected.clear_flag(.variadic))
|
||||
|
||||
if got_typ_sym.symbol_name_except_generic() == expected_typ_sym.symbol_name_except_generic() {
|
||||
// Check if we are making a comparison between two different types of
|
||||
// the same type like `Type<int> and &Type<>`
|
||||
if (got.is_ptr() != expected.is_ptr()) || !c.check_same_module(got, expected) {
|
||||
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
|
||||
} else {
|
||||
// Check on Generics types, there are some case where we have the following case
|
||||
// `&Type<int> == &Type<>`. This is a common case we are implementing a function
|
||||
// with generic parameters like `compare(bst Bst<T> node) {}`
|
||||
if got_typ_sym.symbol_name_except_generic() == expected_typ_sym.symbol_name_except_generic() {
|
||||
// Check if we are making a comparison between two different types of
|
||||
// the same type like `Type<int> and &Type<>`
|
||||
if (got.is_ptr() != expected.is_ptr()) || !c.check_same_module(got, expected) {
|
||||
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
if got == ast.void_type {
|
||||
return error('`$arg.expr` (no value) used as value')
|
||||
}
|
||||
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
|
||||
}
|
||||
|
||||
if got != ast.void_type {
|
||||
return error('cannot use `$got_typ_str` as `$expected_typ_str`')
|
||||
}
|
||||
|
|
@ -627,7 +650,9 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
|
|||
param_elem_info = param_elem_sym.info as ast.Array
|
||||
param_elem_sym = c.table.sym(param_elem_info.elem_type)
|
||||
} else {
|
||||
to_set = arg_elem_info.elem_type
|
||||
if param_elem_sym.name == gt_name {
|
||||
typ = arg_elem_info.elem_type
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -644,7 +669,9 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
|
|||
param_elem_info = param_elem_sym.info as ast.ArrayFixed
|
||||
param_elem_sym = c.table.sym(param_elem_info.elem_type)
|
||||
} else {
|
||||
to_set = arg_elem_info.elem_type
|
||||
if param_elem_sym.name == gt_name {
|
||||
typ = arg_elem_info.elem_type
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -659,6 +686,21 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
|
|||
&& c.table.sym(param_map_info.value_type).name == gt_name {
|
||||
typ = arg_map_info.value_type
|
||||
}
|
||||
} else if arg_sym.kind == .function && param_type_sym.kind == .function {
|
||||
arg_type_func := (arg_sym.info as ast.FnType).func
|
||||
param_type_func := (param_type_sym.info as ast.FnType).func
|
||||
if param_type_func.params.len == arg_type_func.params.len {
|
||||
for n, fn_param in param_type_func.params {
|
||||
if fn_param.typ.has_flag(.generic)
|
||||
&& c.table.sym(fn_param.typ).name == gt_name {
|
||||
typ = arg_type_func.params[n].typ
|
||||
}
|
||||
}
|
||||
if param_type_func.return_type.has_flag(.generic)
|
||||
&& c.table.sym(param_type_func.return_type).name == gt_name {
|
||||
typ = arg_type_func.return_type
|
||||
}
|
||||
}
|
||||
} else if arg_sym.kind in [.struct_, .interface_, .sum_type] {
|
||||
mut generic_types := []ast.Type{}
|
||||
mut concrete_types := []ast.Type{}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ fn all_valid_comptime_idents() []string {
|
|||
return res
|
||||
}
|
||||
|
||||
[heap]
|
||||
[heap; minify]
|
||||
pub struct Checker {
|
||||
pref &pref.Preferences // Preferences shared from V struct
|
||||
pub mut:
|
||||
|
|
@ -644,11 +644,12 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
|||
} else if is_left_type_signed != is_right_type_signed
|
||||
&& left_type != ast.int_literal_type_idx
|
||||
&& right_type != ast.int_literal_type_idx {
|
||||
ls := c.table.type_size(left_type)
|
||||
rs := c.table.type_size(right_type)
|
||||
ls, _ := c.table.type_size(left_type)
|
||||
rs, _ := c.table.type_size(right_type)
|
||||
// prevent e.g. `u32 == i16` but not `u16 == i32` as max_u16 fits in i32
|
||||
// TODO u32 == i32, change < to <=
|
||||
if (is_left_type_signed && ls < rs) || (is_right_type_signed && rs < ls) {
|
||||
if !c.pref.translated && ((is_left_type_signed && ls < rs)
|
||||
|| (is_right_type_signed && rs < ls)) {
|
||||
lt := c.table.sym(left_type).name
|
||||
rt := c.table.sym(right_type).name
|
||||
c.error('`$lt` cannot be compared with `$rt`', node.pos)
|
||||
|
|
@ -1186,7 +1187,9 @@ fn (mut c Checker) fail_if_immutable(expr_ ast.Expr) (string, token.Pos) {
|
|||
}
|
||||
}
|
||||
} else if expr.obj is ast.ConstField && expr.name in c.const_names {
|
||||
if !c.inside_unsafe {
|
||||
if !c.inside_unsafe && !c.pref.translated {
|
||||
// TODO fix this in c2v, do not allow modification of all consts
|
||||
// in translated code
|
||||
c.error('cannot modify constant `$expr.name`', expr.pos)
|
||||
}
|
||||
}
|
||||
|
|
@ -2660,7 +2663,7 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
|||
return c.comptime_call(mut node)
|
||||
}
|
||||
ast.ComptimeSelector {
|
||||
node.left_type = c.unwrap_generic(c.expr(node.left))
|
||||
node.left_type = c.expr(node.left)
|
||||
expr_type := c.unwrap_generic(c.expr(node.field_expr))
|
||||
expr_sym := c.table.sym(expr_type)
|
||||
if expr_type != ast.string_type {
|
||||
|
|
@ -3720,6 +3723,16 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
|
|||
if node.right.op == .amp {
|
||||
c.error('unexpected `&`, expecting expression', node.right.pos)
|
||||
}
|
||||
} else if mut node.right is ast.SelectorExpr {
|
||||
right_sym := c.table.sym(right_type)
|
||||
expr_sym := c.table.sym(node.right.expr_type)
|
||||
if expr_sym.kind == .struct_ && (expr_sym.info as ast.Struct).is_minify
|
||||
&& (node.right.typ == ast.bool_type_idx || (right_sym.kind == .enum_
|
||||
&& !(right_sym.info as ast.Enum).is_flag
|
||||
&& !(right_sym.info as ast.Enum).uses_exprs)) {
|
||||
c.error('cannot take address of field in struct `${c.table.type_to_str(node.right.expr_type)}`, which is tagged as `[minify]`',
|
||||
node.pos.extend(node.right.pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: testing ref/deref strategy
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
|
|||
// return expr.val.i64()
|
||||
// }
|
||||
ast.SizeOf {
|
||||
return c.table.type_size(expr.typ)
|
||||
s, _ := c.table.type_size(expr.typ)
|
||||
return s
|
||||
}
|
||||
ast.FloatLiteral {
|
||||
x := expr.val.f64()
|
||||
|
|
@ -380,19 +381,25 @@ fn (mut c Checker) evaluate_once_comptime_if_attribute(mut node ast.Attr) bool {
|
|||
}
|
||||
}
|
||||
c.inside_ct_attr = true
|
||||
node.ct_skip = c.comptime_if_branch(node.ct_expr, node.pos)
|
||||
node.ct_skip = if c.comptime_if_branch(node.ct_expr, node.pos) == .skip { true } else { false }
|
||||
c.inside_ct_attr = false
|
||||
node.ct_evaled = true
|
||||
return node.ct_skip
|
||||
}
|
||||
|
||||
enum ComptimeBranchSkipState {
|
||||
eval
|
||||
skip
|
||||
unknown
|
||||
}
|
||||
|
||||
// comptime_if_branch checks the condition of a compile-time `if` branch. It returns `true`
|
||||
// if that branch's contents should be skipped (targets a different os for example)
|
||||
fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
||||
fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBranchSkipState {
|
||||
// TODO: better error messages here
|
||||
match cond {
|
||||
ast.BoolLiteral {
|
||||
return !cond.val
|
||||
return if cond.val { .eval } else { .skip }
|
||||
}
|
||||
ast.ParExpr {
|
||||
return c.comptime_if_branch(cond.expr, pos)
|
||||
|
|
@ -401,13 +408,20 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
if cond.op != .not {
|
||||
c.error('invalid `\$if` condition', cond.pos)
|
||||
}
|
||||
return !c.comptime_if_branch(cond.right, cond.pos)
|
||||
reversed := c.comptime_if_branch(cond.right, cond.pos)
|
||||
return if reversed == .eval {
|
||||
.skip
|
||||
} else if reversed == .skip {
|
||||
.eval
|
||||
} else {
|
||||
reversed
|
||||
}
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
if cond.op != .question {
|
||||
c.error('invalid \$if postfix operator', cond.pos)
|
||||
} else if cond.expr is ast.Ident {
|
||||
return cond.expr.name !in c.pref.compile_defines_all
|
||||
return if cond.expr.name in c.pref.compile_defines_all { .eval } else { .skip }
|
||||
} else {
|
||||
c.error('invalid `\$if` condition', cond.pos)
|
||||
}
|
||||
|
|
@ -417,12 +431,18 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
.and {
|
||||
l := c.comptime_if_branch(cond.left, cond.pos)
|
||||
r := c.comptime_if_branch(cond.right, cond.pos)
|
||||
return l || r // skip (return true) if at least one should be skipped
|
||||
if l == .unknown || r == .unknown {
|
||||
return .unknown
|
||||
}
|
||||
return if l == .eval && r == .eval { .eval } else { .skip }
|
||||
}
|
||||
.logical_or {
|
||||
l := c.comptime_if_branch(cond.left, cond.pos)
|
||||
r := c.comptime_if_branch(cond.right, cond.pos)
|
||||
return l && r // skip (return true) only if both should be skipped
|
||||
if l == .unknown || r == .unknown {
|
||||
return .unknown
|
||||
}
|
||||
return if l == .eval || r == .eval { .eval } else { .skip }
|
||||
}
|
||||
.key_is, .not_is {
|
||||
if cond.left is ast.TypeNode && cond.right is ast.TypeNode {
|
||||
|
|
@ -432,15 +452,19 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
c.expr(cond.left)
|
||||
// c.error('`$sym.name` is not an interface', cond.right.pos())
|
||||
}
|
||||
return false
|
||||
return .unknown
|
||||
} else if cond.left is ast.TypeNode && cond.right is ast.ComptimeType {
|
||||
left := cond.left as ast.TypeNode
|
||||
checked_type := c.unwrap_generic(left.typ)
|
||||
return c.table.is_comptime_type(checked_type, cond.right)
|
||||
return if c.table.is_comptime_type(checked_type, cond.right) {
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
}
|
||||
} else if cond.left in [ast.SelectorExpr, ast.TypeNode] {
|
||||
// `$if method.@type is string`
|
||||
c.expr(cond.left)
|
||||
return false
|
||||
return .unknown
|
||||
} else {
|
||||
c.error('invalid `\$if` condition: expected a type or a selector expression or an interface check',
|
||||
cond.left.pos())
|
||||
|
|
@ -455,7 +479,7 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
right_type := c.expr(cond.right)
|
||||
expr := c.find_definition(cond.left) or {
|
||||
c.error(err.msg(), cond.left.pos)
|
||||
return false
|
||||
return .unknown
|
||||
}
|
||||
if !c.check_types(right_type, left_type) {
|
||||
left_name := c.table.type_to_str(left_type)
|
||||
|
|
@ -466,7 +490,19 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
// :)
|
||||
// until `v.eval` is stable, I can't think of a better way to do this
|
||||
different := expr.str() != cond.right.str()
|
||||
return if cond.op == .eq { different } else { !different }
|
||||
return if cond.op == .eq {
|
||||
if different {
|
||||
ComptimeBranchSkipState.skip
|
||||
} else {
|
||||
ComptimeBranchSkipState.eval
|
||||
}
|
||||
} else {
|
||||
if different {
|
||||
ComptimeBranchSkipState.eval
|
||||
} else {
|
||||
ComptimeBranchSkipState.skip
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.error('invalid `\$if` condition: ${cond.left.type_name()}1',
|
||||
cond.pos)
|
||||
|
|
@ -480,51 +516,84 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
ast.Ident {
|
||||
cname := cond.name
|
||||
if cname in valid_comptime_if_os {
|
||||
mut is_os_target_different := false
|
||||
mut is_os_target_equal := true
|
||||
if !c.pref.output_cross_c {
|
||||
target_os := c.pref.os.str().to_lower()
|
||||
is_os_target_different = cname != target_os
|
||||
is_os_target_equal = cname == target_os
|
||||
}
|
||||
return is_os_target_different
|
||||
return if is_os_target_equal { .eval } else { .skip }
|
||||
} else if cname in valid_comptime_if_compilers {
|
||||
return pref.cc_from_string(cname) != c.pref.ccompiler_type
|
||||
return if pref.cc_from_string(cname) == c.pref.ccompiler_type {
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
}
|
||||
} else if cname in valid_comptime_if_platforms {
|
||||
if cname == 'aarch64' {
|
||||
c.note('use `arm64` instead of `aarch64`', pos)
|
||||
}
|
||||
match cname {
|
||||
'amd64' { return c.pref.arch != .amd64 }
|
||||
'i386' { return c.pref.arch != .i386 }
|
||||
'aarch64' { return c.pref.arch != .arm64 }
|
||||
'arm64' { return c.pref.arch != .arm64 }
|
||||
'arm32' { return c.pref.arch != .arm32 }
|
||||
'rv64' { return c.pref.arch != .rv64 }
|
||||
'rv32' { return c.pref.arch != .rv32 }
|
||||
else { return false }
|
||||
'amd64' { return if c.pref.arch == .amd64 { .eval } else { .skip } }
|
||||
'i386' { return if c.pref.arch == .i386 { .eval } else { .skip } }
|
||||
'aarch64' { return if c.pref.arch == .arm64 { .eval } else { .skip } }
|
||||
'arm64' { return if c.pref.arch == .arm64 { .eval } else { .skip } }
|
||||
'arm32' { return if c.pref.arch == .arm32 { .eval } else { .skip } }
|
||||
'rv64' { return if c.pref.arch == .rv64 { .eval } else { .skip } }
|
||||
'rv32' { return if c.pref.arch == .rv32 { .eval } else { .skip } }
|
||||
else { return .unknown }
|
||||
}
|
||||
} else if cname in valid_comptime_if_cpu_features {
|
||||
return false
|
||||
return .unknown
|
||||
} else if cname in valid_comptime_if_other {
|
||||
match cname {
|
||||
'apk' { return !c.pref.is_apk }
|
||||
'js' { return !c.pref.backend.is_js() }
|
||||
'debug' { return !c.pref.is_debug }
|
||||
'prod' { return !c.pref.is_prod }
|
||||
'profile' { return !c.pref.is_prof }
|
||||
'test' { return !c.pref.is_test }
|
||||
'glibc' { return false } // TODO
|
||||
'threads' { return c.table.gostmts == 0 }
|
||||
'prealloc' { return !c.pref.prealloc }
|
||||
'no_bounds_checking' { return cname !in c.pref.compile_defines_all }
|
||||
'freestanding' { return !c.pref.is_bare || c.pref.output_cross_c }
|
||||
'interpreter' { c.pref.backend != .interpret }
|
||||
else { return false }
|
||||
'apk' {
|
||||
return if c.pref.is_apk { .eval } else { .skip }
|
||||
}
|
||||
'js' {
|
||||
return if c.pref.backend.is_js() { .eval } else { .skip }
|
||||
}
|
||||
'debug' {
|
||||
return if c.pref.is_debug { .eval } else { .skip }
|
||||
}
|
||||
'prod' {
|
||||
return if c.pref.is_prod { .eval } else { .skip }
|
||||
}
|
||||
'profile' {
|
||||
return if c.pref.is_prof { .eval } else { .skip }
|
||||
}
|
||||
'test' {
|
||||
return if c.pref.is_test { .eval } else { .skip }
|
||||
}
|
||||
'musl' {
|
||||
return .unknown
|
||||
}
|
||||
'glibc' {
|
||||
return .unknown
|
||||
}
|
||||
'threads' {
|
||||
return if c.table.gostmts > 0 { .eval } else { .skip }
|
||||
}
|
||||
'prealloc' {
|
||||
return if c.pref.prealloc { .eval } else { .skip }
|
||||
}
|
||||
'no_bounds_checking' {
|
||||
return if cname in c.pref.compile_defines_all { .eval } else { .skip }
|
||||
}
|
||||
'freestanding' {
|
||||
return if c.pref.is_bare && !c.pref.output_cross_c { .eval } else { .skip }
|
||||
}
|
||||
'interpreter' {
|
||||
return if c.pref.backend == .interpret { .eval } else { .skip }
|
||||
}
|
||||
else {
|
||||
return .unknown
|
||||
}
|
||||
}
|
||||
} else if cname !in c.pref.compile_defines_all {
|
||||
if cname == 'linux_or_macos' {
|
||||
c.error('linux_or_macos is deprecated, use `\$if linux || macos {` instead',
|
||||
cond.pos)
|
||||
return false
|
||||
return .unknown
|
||||
}
|
||||
// `$if some_var {}`, or `[if user_defined_tag] fn abc(){}`
|
||||
typ := c.unwrap_generic(c.expr(cond))
|
||||
|
|
@ -533,11 +602,11 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
if !c.inside_ct_attr {
|
||||
c.error('unknown var: `$cname`', pos)
|
||||
}
|
||||
return false
|
||||
return .unknown
|
||||
}
|
||||
expr := c.find_obj_definition(cond.obj) or {
|
||||
c.error(err.msg(), cond.pos)
|
||||
return false
|
||||
return .unknown
|
||||
}
|
||||
if !c.check_types(typ, ast.bool_type) {
|
||||
type_name := c.table.type_to_str(typ)
|
||||
|
|
@ -545,21 +614,22 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
|||
}
|
||||
// :)
|
||||
// until `v.eval` is stable, I can't think of a better way to do this
|
||||
return !(expr as ast.BoolLiteral).val
|
||||
return if (expr as ast.BoolLiteral).val { .eval } else { .skip }
|
||||
}
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
if cond.is_pkgconfig {
|
||||
mut m := pkgconfig.main([cond.args_var]) or {
|
||||
c.error(err.msg(), cond.pos)
|
||||
return true
|
||||
return .skip
|
||||
}
|
||||
m.run() or { return true }
|
||||
m.run() or { return .skip }
|
||||
}
|
||||
return .eval
|
||||
}
|
||||
else {
|
||||
c.error('invalid `\$if` condition', pos)
|
||||
}
|
||||
}
|
||||
return false
|
||||
return .unknown
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ pub fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
|
|||
}
|
||||
|
||||
fn (mut c Checker) check_array_init_para_type(para string, expr ast.Expr, pos token.Pos) {
|
||||
sym := c.table.sym(c.expr(expr))
|
||||
sym := c.table.sym(c.unwrap_generic(c.expr(expr)))
|
||||
if sym.kind !in [.int, .int_literal] {
|
||||
c.error('array $para needs to be an int', pos)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1778,6 +1778,11 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, node ast
|
|||
c.error('type mismatch, `$arg_expr.name` must return a bool', arg_expr.pos)
|
||||
}
|
||||
}
|
||||
ast.StringLiteral, ast.StringInterLiteral {
|
||||
if !is_map {
|
||||
c.error('type mismatch, should use e.g. `${node.name}(it > 2)`', arg_expr.pos)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
node.typ = ast.void_type
|
||||
mut nbranches_with_return := 0
|
||||
mut nbranches_without_return := 0
|
||||
mut should_skip := false // Whether the current branch should be skipped
|
||||
mut skip_state := ComptimeBranchSkipState.unknown
|
||||
mut found_branch := false // Whether a matching branch was found- skip the rest
|
||||
mut is_comptime_type_is_expr := false // if `$if T is string`
|
||||
for i in 0 .. node.branches.len {
|
||||
|
|
@ -41,8 +41,8 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
}
|
||||
if !node.has_else || i < node.branches.len - 1 {
|
||||
if node.is_comptime {
|
||||
should_skip = c.comptime_if_branch(branch.cond, branch.pos)
|
||||
node.branches[i].pkg_exist = !should_skip
|
||||
skip_state = c.comptime_if_branch(branch.cond, branch.pos)
|
||||
node.branches[i].pkg_exist = if skip_state == .eval { true } else { false }
|
||||
} else {
|
||||
// check condition type is boolean
|
||||
c.expected_type = ast.bool_type
|
||||
|
|
@ -67,7 +67,11 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
if branch.cond.right is ast.ComptimeType && left is ast.TypeNode {
|
||||
is_comptime_type_is_expr = true
|
||||
checked_type := c.unwrap_generic(left.typ)
|
||||
should_skip = !c.table.is_comptime_type(checked_type, branch.cond.right as ast.ComptimeType)
|
||||
skip_state = if c.table.is_comptime_type(checked_type, branch.cond.right as ast.ComptimeType) {
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
}
|
||||
} else {
|
||||
got_type := c.unwrap_generic((branch.cond.right as ast.TypeNode).typ)
|
||||
sym := c.table.sym(got_type)
|
||||
|
|
@ -84,14 +88,17 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
is_comptime_type_is_expr = true
|
||||
// is interface
|
||||
checked_type := c.unwrap_generic(left.typ)
|
||||
should_skip = !c.table.does_type_implement_interface(checked_type,
|
||||
skip_state = if c.table.does_type_implement_interface(checked_type,
|
||||
got_type)
|
||||
{
|
||||
.eval
|
||||
} else {
|
||||
.skip
|
||||
}
|
||||
} else if left is ast.TypeNode {
|
||||
is_comptime_type_is_expr = true
|
||||
left_type := c.unwrap_generic(left.typ)
|
||||
if left_type != got_type {
|
||||
should_skip = true
|
||||
}
|
||||
skip_state = if left_type == got_type { .eval } else { .skip }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,10 +106,10 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
cur_skip_flags := c.skip_flags
|
||||
if found_branch {
|
||||
c.skip_flags = true
|
||||
} else if should_skip {
|
||||
} else if skip_state == .skip {
|
||||
c.skip_flags = true
|
||||
should_skip = false // Reset the value of `should_skip` for the next branch
|
||||
} else if !is_comptime_type_is_expr {
|
||||
skip_state = .unknown // Reset the value of `skip_state` for the next branch
|
||||
} else if !is_comptime_type_is_expr && skip_state == .eval {
|
||||
found_branch = true // If a branch wasn't skipped, the rest must be
|
||||
}
|
||||
if c.fn_level == 0 && c.pref.output_cross_c {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if struct_sym.info.is_minify {
|
||||
node.fields.sort_with_compare(minify_sort_fn)
|
||||
struct_sym.info.fields.sort_with_compare(minify_sort_fn)
|
||||
}
|
||||
for attr in node.attrs {
|
||||
if attr.name == 'typedef' && node.language != .c {
|
||||
c.error('`typedef` attribute can only be used with C structs', node.pos)
|
||||
|
|
@ -124,6 +128,62 @@ pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
|||
}
|
||||
}
|
||||
|
||||
fn minify_sort_fn(a &ast.StructField, b &ast.StructField) int {
|
||||
if a.typ == b.typ {
|
||||
return 0
|
||||
}
|
||||
// push all bool fields to the end of the struct
|
||||
if a.typ == ast.bool_type_idx {
|
||||
if b.typ == ast.bool_type_idx {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
} else if b.typ == ast.bool_type_idx {
|
||||
return -1
|
||||
}
|
||||
|
||||
mut t := global_table
|
||||
a_sym := t.sym(a.typ)
|
||||
b_sym := t.sym(b.typ)
|
||||
|
||||
// push all non-flag enums to the end too, just before the bool fields
|
||||
// TODO: support enums with custom field values as well
|
||||
if a_sym.info is ast.Enum {
|
||||
if !a_sym.info.is_flag && !a_sym.info.uses_exprs {
|
||||
if b_sym.kind == .enum_ {
|
||||
a_nr_vals := (a_sym.info as ast.Enum).vals.len
|
||||
b_nr_vals := (b_sym.info as ast.Enum).vals.len
|
||||
return if a_nr_vals > b_nr_vals {
|
||||
-1
|
||||
} else if a_nr_vals < b_nr_vals {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
} else if b_sym.info is ast.Enum {
|
||||
if !b_sym.info.is_flag && !b_sym.info.uses_exprs {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
a_size, a_align := t.type_size(a.typ)
|
||||
b_size, b_align := t.type_size(b.typ)
|
||||
return if a_align > b_align {
|
||||
-1
|
||||
} else if a_align < b_align {
|
||||
1
|
||||
} else if a_size > b_size {
|
||||
-1
|
||||
} else if a_size < b_size {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
||||
if node.typ == ast.void_type {
|
||||
// short syntax `foo(key:val, key2:val2)`
|
||||
|
|
@ -267,6 +327,11 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
|||
node.pos)
|
||||
}
|
||||
}
|
||||
mut info_fields_sorted := []ast.StructField{}
|
||||
if node.is_short {
|
||||
info_fields_sorted = info.fields.clone()
|
||||
info_fields_sorted.sort(a.i < b.i)
|
||||
}
|
||||
mut inited_fields := []string{}
|
||||
for i, mut field in node.fields {
|
||||
mut field_info := ast.StructField{}
|
||||
|
|
@ -277,7 +342,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
|||
// We should just stop here.
|
||||
break
|
||||
}
|
||||
field_info = info.fields[i]
|
||||
field_info = info_fields_sorted[i]
|
||||
field_name = field_info.name
|
||||
node.fields[i].name = field_name
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
vlib/v/checker/tests/array_filter_arg_mismatch_err.vv:17:20: error: type mismatch, should use e.g. `filter(it > 2)`
|
||||
15 | }]
|
||||
16 |
|
||||
17 | a1 := list.filter('it.value > 2')
|
||||
| ~~~~~~~~~~~~~~
|
||||
18 | println(a1)
|
||||
19 |
|
||||
vlib/v/checker/tests/array_filter_arg_mismatch_err.vv:20:17: error: type mismatch, should use e.g. `any(it > 2)`
|
||||
18 | println(a1)
|
||||
19 |
|
||||
20 | a2 := list.any('it.value > 2')
|
||||
| ~~~~~~~~~~~~~~
|
||||
21 | println(a2)
|
||||
22 |
|
||||
vlib/v/checker/tests/array_filter_arg_mismatch_err.vv:23:17: error: type mismatch, should use e.g. `all(it > 2)`
|
||||
21 | println(a2)
|
||||
22 |
|
||||
23 | a3 := list.all('it.value > 2')
|
||||
| ~~~~~~~~~~~~~~
|
||||
24 | println(a3)
|
||||
25 | }
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
module main
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
value int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut list := [Row{
|
||||
value: 1
|
||||
}, Row{
|
||||
value: 2
|
||||
}, Row{
|
||||
value: 3
|
||||
}]
|
||||
|
||||
a1 := list.filter('it.value > 2')
|
||||
println(a1)
|
||||
|
||||
a2 := list.any('it.value > 2')
|
||||
println(a2)
|
||||
|
||||
a3 := list.all('it.value > 2')
|
||||
println(a3)
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/comptime_selector_expr_type_err.vv:12:27: cgen error: `a` has no field named `value`
|
||||
10 | for i in a {
|
||||
11 | $for field in Row.fields {
|
||||
12 | println('field $i: ' + a.$(field.name).str() )
|
||||
| ^
|
||||
13 | }
|
||||
14 | }
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
module main
|
||||
|
||||
pub struct Row {
|
||||
pub mut:
|
||||
value string
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := []Row{}
|
||||
for i in a {
|
||||
$for field in Row.fields {
|
||||
println('field $i: ' + a.$(field.name).str() )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/checker/tests/fn_call_arg_mismatch.vv:7:9: error: cannot use `&[]int` as `[]int` in argument 1 to `abc`
|
||||
vlib/v/checker/tests/fn_call_arg_mismatch_err_a.vv:7:9: error: cannot use `&[]int` as `[]int` in argument 1 to `abc`
|
||||
5 | fn main() {
|
||||
6 | a := [1, 2, 3]
|
||||
7 | go abc(&a)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/fn_call_arg_mismatch_err_b.vv:2:6: error: `bar()` (no value) used as value in argument 1 to `foo`
|
||||
1 | fn main() {
|
||||
2 | foo(bar())
|
||||
| ~~~~~
|
||||
3 | }
|
||||
4 |
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fn main() {
|
||||
foo(bar())
|
||||
}
|
||||
|
||||
fn foo(x int) {}
|
||||
|
||||
fn bar() {}
|
||||
|
|
@ -92,6 +92,7 @@ pub fn (sk SymbolKind) str() string {
|
|||
}
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Doc {
|
||||
pub mut:
|
||||
prefs &pref.Preferences = new_vdoc_preferences()
|
||||
|
|
@ -121,6 +122,7 @@ pub mut:
|
|||
platform Platform
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct DocNode {
|
||||
pub mut:
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -62,13 +62,12 @@ pub fn (dc DocNode) merge_comments_without_examples() string {
|
|||
if dc.comments[i].is_multi_line_example() {
|
||||
i++
|
||||
if i == dc.comments.len || !dc.comments[i].has_triple_backtick() {
|
||||
eprintln('$dc.file_path:$dc.pos.line_nr: Expected code block after empty example line:')
|
||||
eprintln('$dc.file_path:$dc.pos.line_nr: warning: expected code block after empty example line:')
|
||||
eprintln('// ```')
|
||||
if i < dc.comments.len {
|
||||
eprintln('Found:')
|
||||
eprintln('//' + dc.comments[i].text[1..])
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
i++
|
||||
for i < dc.comments.len && !dc.comments[i].has_triple_backtick() {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub enum Reporter {
|
|||
gen
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Error {
|
||||
pub:
|
||||
message string
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub enum CommentsLevel {
|
|||
// - level: either .keep (don't indent), or .indent (increment indentation)
|
||||
// - iembed: a /* ... */ block comment used inside expressions; // comments the whole line
|
||||
// - prev_line: the line number of the previous token to save linebreaks
|
||||
[params]
|
||||
[minify; params]
|
||||
pub struct CommentsOptions {
|
||||
has_nl bool = true
|
||||
inline bool
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const (
|
|||
max_len = [0, 35, 60, 85, 93, 100]
|
||||
)
|
||||
|
||||
[minify]
|
||||
pub struct Fmt {
|
||||
pub mut:
|
||||
file ast.File
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module c
|
|||
import strings
|
||||
import v.ast
|
||||
|
||||
fn (mut g Gen) array_init(node ast.ArrayInit) {
|
||||
fn (mut g Gen) array_init(node ast.ArrayInit, var_name string) {
|
||||
array_type := g.unwrap(node.typ)
|
||||
mut array_styp := ''
|
||||
elem_type := g.unwrap(node.elem_type)
|
||||
|
|
@ -24,10 +24,10 @@ fn (mut g Gen) array_init(node ast.ArrayInit) {
|
|||
}
|
||||
len := node.exprs.len
|
||||
if array_type.unaliased_sym.kind == .array_fixed {
|
||||
g.fixed_array_init(node, array_type)
|
||||
g.fixed_array_init(node, array_type, var_name)
|
||||
} else if len == 0 {
|
||||
// `[]int{len: 6, cap:10, init:22}`
|
||||
g.array_init_with_fields(node, elem_type, is_amp, shared_styp)
|
||||
g.array_init_with_fields(node, elem_type, is_amp, shared_styp, var_name)
|
||||
} else {
|
||||
// `[1, 2, 3]`
|
||||
elem_styp := g.typ(elem_type.typ)
|
||||
|
|
@ -70,17 +70,24 @@ fn (mut g Gen) array_init(node ast.ArrayInit) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) fixed_array_init(node ast.ArrayInit, array_type Type) {
|
||||
fn (mut g Gen) fixed_array_init(node ast.ArrayInit, array_type Type, var_name string) {
|
||||
if node.has_it {
|
||||
g.inside_lambda = true
|
||||
tmp := g.new_tmp_var()
|
||||
mut s := g.go_before_stmt(0)
|
||||
mut tmp := g.new_tmp_var()
|
||||
mut s := ''
|
||||
if var_name.len != 0 {
|
||||
tmp = var_name
|
||||
} else {
|
||||
s = g.go_before_stmt(0)
|
||||
}
|
||||
s_ends_with_ln := s.ends_with('\n')
|
||||
s = s.trim_space()
|
||||
ret_typ := g.typ(node.typ)
|
||||
elem_typ := g.typ(node.elem_type)
|
||||
g.empty_line = true
|
||||
g.write('$ret_typ $tmp =')
|
||||
if var_name.len == 0 {
|
||||
g.write('$ret_typ $tmp =')
|
||||
}
|
||||
g.write('{')
|
||||
if node.has_val {
|
||||
for i, expr in node.exprs {
|
||||
|
|
@ -117,12 +124,14 @@ fn (mut g Gen) fixed_array_init(node ast.ArrayInit, array_type Type) {
|
|||
g.writeln('}')
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
if s_ends_with_ln {
|
||||
g.writeln(s)
|
||||
} else {
|
||||
g.write(s)
|
||||
if var_name.len == 0 {
|
||||
if s_ends_with_ln {
|
||||
g.writeln(s)
|
||||
} else {
|
||||
g.write(s)
|
||||
}
|
||||
g.write(tmp)
|
||||
}
|
||||
g.write(tmp)
|
||||
g.inside_lambda = false
|
||||
return
|
||||
}
|
||||
|
|
@ -166,21 +175,28 @@ fn (mut g Gen) fixed_array_init(node ast.ArrayInit, array_type Type) {
|
|||
}
|
||||
|
||||
// `[]int{len: 6, cap: 10, init: it * it}`
|
||||
fn (mut g Gen) array_init_with_fields(node ast.ArrayInit, elem_type Type, is_amp bool, shared_styp string) {
|
||||
fn (mut g Gen) array_init_with_fields(node ast.ArrayInit, elem_type Type, is_amp bool, shared_styp string, var_name string) {
|
||||
elem_styp := g.typ(elem_type.typ)
|
||||
noscan := g.check_noscan(elem_type.typ)
|
||||
is_default_array := elem_type.unaliased_sym.kind == .array && node.has_default
|
||||
is_default_map := elem_type.unaliased_sym.kind == .map && node.has_default
|
||||
if node.has_it { // []int{len: 6, init: it * it} when variable it is used in init expression
|
||||
g.inside_lambda = true
|
||||
tmp := g.new_tmp_var()
|
||||
mut s := g.go_before_stmt(0)
|
||||
mut tmp := g.new_tmp_var()
|
||||
mut s := ''
|
||||
if var_name.len != 0 {
|
||||
tmp = var_name
|
||||
} else {
|
||||
s = g.go_before_stmt(0)
|
||||
}
|
||||
s_ends_with_ln := s.ends_with('\n')
|
||||
s = s.trim_space()
|
||||
ret_typ := g.typ(node.typ)
|
||||
elem_typ := g.typ(node.elem_type)
|
||||
g.empty_line = true
|
||||
g.write('$ret_typ $tmp =')
|
||||
if var_name.len == 0 {
|
||||
g.write('$ret_typ $tmp =')
|
||||
}
|
||||
if is_default_array {
|
||||
g.write('__new_array_with_array_default${noscan}(')
|
||||
} else if is_default_map {
|
||||
|
|
@ -238,12 +254,14 @@ fn (mut g Gen) array_init_with_fields(node ast.ArrayInit, elem_type Type, is_amp
|
|||
g.writeln('}')
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
if s_ends_with_ln {
|
||||
g.writeln(s)
|
||||
} else {
|
||||
g.write(s)
|
||||
if var_name.len == 0 {
|
||||
if s_ends_with_ln {
|
||||
g.writeln(s)
|
||||
} else {
|
||||
g.write(s)
|
||||
}
|
||||
g.write(tmp)
|
||||
}
|
||||
g.write(tmp)
|
||||
g.inside_lambda = false
|
||||
return
|
||||
}
|
||||
|
|
@ -682,43 +700,86 @@ fn (mut g Gen) gen_array_contains_methods() {
|
|||
continue
|
||||
}
|
||||
done << t
|
||||
mut fn_builder := strings.new_builder(512)
|
||||
mut left_type_str := g.typ(t)
|
||||
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)
|
||||
if elem_sym.kind == .function {
|
||||
left_type_str = 'Array_voidptr'
|
||||
elem_type_str = 'voidptr'
|
||||
}
|
||||
g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto')
|
||||
mut fn_builder := strings.new_builder(512)
|
||||
fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {')
|
||||
fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {')
|
||||
if elem_sym.kind == .string {
|
||||
fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {')
|
||||
} else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_sym.kind == .function {
|
||||
fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {')
|
||||
} else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_sym.kind == .interface_ && left_info.elem_type.nr_muls() == 0 {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_sym.kind == .sum_type && left_info.elem_type.nr_muls() == 0 {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_sym.kind == .alias && left_info.elem_type.nr_muls() == 0 {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else {
|
||||
fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {')
|
||||
|
||||
if left_final_sym.kind == .array {
|
||||
elem_type := (left_final_sym.info as ast.Array).elem_type
|
||||
mut elem_type_str := g.typ(elem_type)
|
||||
elem_kind := g.table.sym(elem_type).kind
|
||||
elem_is_not_ptr := elem_type.nr_muls() == 0
|
||||
if elem_kind == .function {
|
||||
left_type_str = 'Array_voidptr'
|
||||
elem_type_str = 'voidptr'
|
||||
}
|
||||
g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto')
|
||||
fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {')
|
||||
fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {')
|
||||
if elem_kind == .string {
|
||||
fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {')
|
||||
} else if elem_kind == .array && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_kind == .function {
|
||||
fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {')
|
||||
} else if elem_kind == .map && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_kind == .struct_ && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_kind == .interface_ && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_kind == .sum_type && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else if elem_kind == .alias && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq((($elem_type_str*)a.data)[i], v)) {')
|
||||
} else {
|
||||
fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {')
|
||||
}
|
||||
} else if left_final_sym.kind == .array_fixed {
|
||||
left_info := left_final_sym.info as ast.ArrayFixed
|
||||
size := left_info.size
|
||||
elem_type := left_info.elem_type
|
||||
mut elem_type_str := g.typ(elem_type)
|
||||
elem_kind := g.table.sym(elem_type).kind
|
||||
elem_is_not_ptr := elem_type.nr_muls() == 0
|
||||
if elem_kind == .function {
|
||||
left_type_str = 'Array_voidptr'
|
||||
elem_type_str = 'voidptr'
|
||||
}
|
||||
g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto')
|
||||
fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {')
|
||||
fn_builder.writeln('\tfor (int i = 0; i < $size; ++i) {')
|
||||
if elem_kind == .string {
|
||||
fn_builder.writeln('\t\tif (fast_string_eq(a[i], v)) {')
|
||||
} else if elem_kind == .array && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(left_info.elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(a[i], v)) {')
|
||||
} else if elem_kind == .function {
|
||||
fn_builder.writeln('\t\tif (a[i] == v) {')
|
||||
} else if elem_kind == .map && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(a[i], v)) {')
|
||||
} else if elem_kind == .struct_ && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(a[i], v)) {')
|
||||
} else if elem_kind == .interface_ && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_interface_eq(a[i], v)) {')
|
||||
} else if elem_kind == .sum_type && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_sumtype_eq(a[i], v)) {')
|
||||
} else if elem_kind == .alias && elem_is_not_ptr {
|
||||
ptr_typ := g.equality_fn(elem_type)
|
||||
fn_builder.writeln('\t\tif (${ptr_typ}_alias_eq(a[i], v)) {')
|
||||
} else {
|
||||
fn_builder.writeln('\t\tif (a[i] == v) {')
|
||||
}
|
||||
}
|
||||
fn_builder.writeln('\t\t\treturn true;')
|
||||
fn_builder.writeln('\t\t}')
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
|
|||
} else if is_decl {
|
||||
if is_fixed_array_init && !has_val {
|
||||
if val is ast.ArrayInit {
|
||||
g.array_init(val)
|
||||
g.array_init(val, ident.name)
|
||||
} else {
|
||||
g.write('{0}')
|
||||
}
|
||||
|
|
@ -451,7 +451,11 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
|
|||
if val.is_auto_deref_var() {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(val)
|
||||
if val is ast.ArrayInit {
|
||||
g.array_init(val, ident.name)
|
||||
} else {
|
||||
g.expr(val)
|
||||
}
|
||||
if is_auto_heap {
|
||||
g.write('))')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2879,7 +2879,7 @@ fn (mut g Gen) expr(node_ ast.Expr) {
|
|||
g.expr(node.expr)
|
||||
}
|
||||
ast.ArrayInit {
|
||||
g.array_init(node)
|
||||
g.array_init(node, '')
|
||||
}
|
||||
ast.AsCast {
|
||||
g.as_cast(node)
|
||||
|
|
@ -4377,7 +4377,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
|
|||
if field.is_simple_define_const() {
|
||||
// "Simple" expressions are not going to need multiple statements,
|
||||
// only the ones which are inited later, so it's safe to use expr_string
|
||||
g.const_decl_simple_define(name, g.expr_string(field_expr))
|
||||
g.const_decl_simple_define(field.name, g.expr_string(field_expr))
|
||||
} else {
|
||||
g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
|
||||
}
|
||||
|
|
@ -4479,7 +4479,17 @@ fn (mut g Gen) const_decl_simple_define(name string, val string) {
|
|||
// so that we don't pollute the binary with unnecessary global vars
|
||||
// Do not do this when building a module, otherwise the consts
|
||||
// will not be accessible.
|
||||
g.definitions.write_string('#define _const_$name ')
|
||||
mut x := util.no_dots(name)
|
||||
if g.pref.translated && !g.is_builtin_mod && !util.module_is_builtin(name.all_before_last('.')) {
|
||||
// Don't prepend "_const" to translated C consts,
|
||||
// but only in user code, continue prepending "_const" to builtin consts.
|
||||
if x.starts_with('main__') {
|
||||
x = x['main__'.len..]
|
||||
}
|
||||
} else {
|
||||
x = '_const_$x'
|
||||
}
|
||||
g.definitions.write_string('#define $x ')
|
||||
g.definitions.writeln(val)
|
||||
}
|
||||
|
||||
|
|
@ -4801,6 +4811,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
|
|||
}
|
||||
}
|
||||
|
||||
is_minify := sym.info.is_minify
|
||||
g.type_definitions.writeln(pre_pragma)
|
||||
|
||||
if sym.info.is_union {
|
||||
|
|
@ -4835,7 +4846,26 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
|
|||
type_name := g.typ(field.typ)
|
||||
field_name := c_name(field.name)
|
||||
volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
|
||||
g.type_definitions.writeln('\t$volatile_prefix$type_name $field_name;')
|
||||
mut size_suffix := ''
|
||||
if is_minify && !g.is_cc_msvc {
|
||||
if field.typ == ast.bool_type_idx {
|
||||
size_suffix = ' : 1'
|
||||
} else {
|
||||
field_sym := g.table.sym(field.typ)
|
||||
if field_sym.info is ast.Enum {
|
||||
if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
|
||||
mut bits_needed := 0
|
||||
mut l := field_sym.info.vals.len
|
||||
for l > 0 {
|
||||
bits_needed++
|
||||
l >>= 1
|
||||
}
|
||||
size_suffix = ' : $bits_needed'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
g.type_definitions.writeln('\t$volatile_prefix$type_name $field_name$size_suffix;')
|
||||
}
|
||||
} else {
|
||||
g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;')
|
||||
|
|
|
|||
|
|
@ -366,6 +366,10 @@ const c_common_macros = '
|
|||
#undef VWEAK
|
||||
#define VWEAK
|
||||
#endif
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
#undef VWEAK
|
||||
#define VWEAK
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(VNORETURN)
|
||||
|
|
@ -460,11 +464,6 @@ typedef int (*qsort_callback_func)(const void*, const void*);
|
|||
#if defined __has_include
|
||||
#if __has_include (<execinfo.h>)
|
||||
#include <execinfo.h>
|
||||
#else
|
||||
// Most probably musl OR __ANDROID__ ...
|
||||
int backtrace (void **__array, int __size) { return 0; }
|
||||
char **backtrace_symbols (void *const *__array, int __size){ return 0; }
|
||||
void backtrace_symbols_fd (void *const *__array, int __size, int __fd){}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,7 +20,12 @@ fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) {
|
|||
if node.field_expr.expr is ast.Ident {
|
||||
if node.field_expr.expr.name == g.comptime_for_field_var
|
||||
&& node.field_expr.field_name == 'name' {
|
||||
g.write(c_name(g.comptime_for_field_value.name))
|
||||
field_name := g.comptime_for_field_value.name
|
||||
left_sym := g.table.sym(g.unwrap_generic(node.left_type))
|
||||
_ := g.table.find_field_with_embeds(left_sym, field_name) or {
|
||||
g.error('`$node.left` has no field named `$field_name`', node.left.pos())
|
||||
}
|
||||
g.write(c_name(field_name))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -416,8 +416,9 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
|
|||
}
|
||||
for attr in node.attrs {
|
||||
if attr.name == 'export' {
|
||||
weak := if node.attrs.any(it.name == 'weak') { 'VWEAK ' } else { '' }
|
||||
g.writeln('// export alias: $attr.arg -> $name')
|
||||
export_alias := '$type_name $fn_attrs${attr.arg}($arg_str)'
|
||||
export_alias := '$weak$type_name $fn_attrs${attr.arg}($arg_str)'
|
||||
g.definitions.writeln('VV_EXPORTED_SYMBOL $export_alias; // exported fn $node.name')
|
||||
g.writeln('$export_alias {')
|
||||
g.write('\treturn ${name}(')
|
||||
|
|
@ -492,7 +493,8 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) {
|
|||
g.indent--
|
||||
ps := g.table.pointer_size
|
||||
is_big_cutoff := if g.pref.os == .windows || g.pref.arch == .arm32 { ps } else { ps * 2 }
|
||||
is_big := g.table.type_size(node.decl.return_type) > is_big_cutoff
|
||||
rt_size, _ := g.table.type_size(node.decl.return_type)
|
||||
is_big := rt_size > is_big_cutoff
|
||||
g.write('}, sizeof($ctx_struct)))')
|
||||
|
||||
mut sb := strings.new_builder(512)
|
||||
|
|
@ -2142,6 +2144,10 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
|
|||
g.write('__NOINLINE ')
|
||||
}
|
||||
'weak' {
|
||||
if attrs.any(it.name == 'export') {
|
||||
// only the exported wrapper should be weak; otherwise x86_64-w64-mingw32-gcc complains
|
||||
continue
|
||||
}
|
||||
// a `[weak]` tag tells the C compiler, that the next declaration will be weak, i.e. when linking,
|
||||
// if there is another declaration of a symbol with the same name (a 'strong' one), it should be
|
||||
// used instead, *without linker errors about duplicate symbols*.
|
||||
|
|
|
|||
|
|
@ -472,7 +472,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
|
|||
return
|
||||
}
|
||||
}
|
||||
if right.sym.info is ast.Array {
|
||||
if right.sym.info is ast.ArrayFixed {
|
||||
elem_type := right.sym.info.elem_type
|
||||
elem_type_ := g.unwrap(elem_type)
|
||||
if elem_type_.sym.kind == .sum_type {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
#if defined(__GLIBC__)
|
||||
x = 2;
|
||||
#else
|
||||
x = 3;
|
||||
#endif
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fn main() {
|
||||
mut x := 1
|
||||
$if glibc {
|
||||
x = 2
|
||||
} $else {
|
||||
x = 3
|
||||
}
|
||||
println('done')
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
VV_LOCAL_SYMBOL int main__my_other_fn(void);
|
||||
VV_EXPORTED_SYMBOL VWEAK int wxyz(void); // exported fn main.my_other_fn
|
||||
VWEAK int wxyz(void) {
|
||||
VV_LOCAL_SYMBOL int main__my_other_fn(void) {
|
||||
|
||||
VV_LOCAL_SYMBOL int main__my_fn(void);
|
||||
VV_EXPORTED_SYMBOL int abcd(void); // exported fn main.my_fn
|
||||
int abcd(void) {
|
||||
VV_LOCAL_SYMBOL int main__my_fn(void) {
|
||||
|
||||
println(int_str(main__my_fn()));
|
||||
println(int_str(main__my_other_fn()));
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
42
|
||||
11
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// This file tests the ability to export functions to C with a fully custom name.
|
||||
|
||||
// It also tests, that the exported functions will be exported as weak symbols,
|
||||
// if the user tagged them as such.
|
||||
|
||||
[export: abcd]
|
||||
fn my_fn() int {
|
||||
return 42
|
||||
}
|
||||
|
||||
[export: wxyz]
|
||||
[weak]
|
||||
fn my_other_fn() int {
|
||||
return 11
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println(my_fn())
|
||||
println(my_other_fn())
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ mut:
|
|||
// XXX WHY gen_exit fn (expr ast.Expr)
|
||||
}
|
||||
|
||||
[heap]
|
||||
[heap; minify]
|
||||
pub struct Gen {
|
||||
out_name string
|
||||
pref &pref.Preferences // Preferences shared from V struct
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub type FNLinkLiveSymbols = fn (linkcb voidptr)
|
|||
|
||||
pub type FNLiveReloadCB = fn (info &LiveReloadInfo)
|
||||
|
||||
[minify]
|
||||
pub struct LiveReloadInfo {
|
||||
pub:
|
||||
vexe string // full path to the v compiler
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
|||
}
|
||||
last_pos = p.tok.pos()
|
||||
p.check(.rsbr)
|
||||
if exprs.len == 1 && p.tok.kind in [.name, .amp, .lsbr] && p.tok.line_nr == line_nr {
|
||||
if exprs.len == 1 && p.tok.line_nr == line_nr
|
||||
&& (p.tok.kind in [.name, .amp] || (p.tok.kind == .lsbr && p.is_array_type())) {
|
||||
// [100]u8
|
||||
elem_type = p.parse_type()
|
||||
if p.table.sym(elem_type).name == 'byte' {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import v.errors
|
|||
import os
|
||||
import hash.fnv1a
|
||||
|
||||
[minify]
|
||||
pub struct Parser {
|
||||
pref &pref.Preferences
|
||||
mut:
|
||||
|
|
@ -479,6 +480,27 @@ pub fn (p &Parser) peek_token_after_var_list() token.Token {
|
|||
return tok
|
||||
}
|
||||
|
||||
fn (p &Parser) is_array_type() bool {
|
||||
mut i := 1
|
||||
mut tok := p.tok
|
||||
line_nr := p.tok.line_nr
|
||||
|
||||
for {
|
||||
tok = p.peek_token(i)
|
||||
if tok.line_nr != line_nr {
|
||||
return false
|
||||
}
|
||||
if tok.kind in [.name, .amp] {
|
||||
return true
|
||||
}
|
||||
i++
|
||||
if tok.kind == .lsbr || tok.kind != .rsbr {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) open_scope() {
|
||||
p.scope = &ast.Scope{
|
||||
parent: p.scope
|
||||
|
|
@ -3480,6 +3502,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
mut vals := []string{}
|
||||
// mut default_exprs := []ast.Expr{}
|
||||
mut fields := []ast.EnumField{}
|
||||
mut uses_exprs := false
|
||||
for p.tok.kind != .eof && p.tok.kind != .rcbr {
|
||||
pos := p.tok.pos()
|
||||
val := p.check_name()
|
||||
|
|
@ -3491,6 +3514,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
p.next()
|
||||
expr = p.expr(0)
|
||||
has_expr = true
|
||||
uses_exprs = true
|
||||
}
|
||||
fields << ast.EnumField{
|
||||
name: val
|
||||
|
|
@ -3538,6 +3562,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||
vals: vals
|
||||
is_flag: is_flag
|
||||
is_multi_allowed: is_multi_allowed
|
||||
uses_exprs: uses_exprs
|
||||
}
|
||||
is_pub: is_pub
|
||||
})
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
mut end_comments := []ast.Comment{}
|
||||
if !no_body {
|
||||
p.check(.lcbr)
|
||||
mut i := 0
|
||||
for p.tok.kind != .rcbr {
|
||||
mut comments := []ast.Comment{}
|
||||
for p.tok.kind == .comment {
|
||||
|
|
@ -267,6 +268,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
pos: field_pos
|
||||
type_pos: type_pos
|
||||
comments: comments
|
||||
i: i
|
||||
default_expr: default_expr
|
||||
has_default_expr: has_default_expr
|
||||
attrs: p.attrs
|
||||
|
|
@ -283,6 +285,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
pos: field_pos
|
||||
type_pos: type_pos
|
||||
comments: comments
|
||||
i: i
|
||||
default_expr: default_expr
|
||||
has_default_expr: has_default_expr
|
||||
attrs: p.attrs
|
||||
|
|
@ -292,12 +295,14 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
is_volatile: is_field_volatile
|
||||
}
|
||||
p.attrs = []
|
||||
i++
|
||||
}
|
||||
p.top_level_statement_end()
|
||||
last_line = p.tok.line_nr
|
||||
p.check(.rcbr)
|
||||
}
|
||||
t := ast.TypeSymbol{
|
||||
is_minify := attrs.contains('minify')
|
||||
mut t := ast.TypeSymbol{
|
||||
kind: .struct_
|
||||
language: language
|
||||
name: name
|
||||
|
|
@ -309,6 +314,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
is_typedef: attrs.contains('typedef')
|
||||
is_union: is_union
|
||||
is_heap: attrs.contains('heap')
|
||||
is_minify: is_minify
|
||||
is_generic: generic_types.len > 0
|
||||
generic_types: generic_types
|
||||
attrs: attrs
|
||||
|
|
@ -512,9 +518,9 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
// Parse fields or methods
|
||||
mut fields := []ast.StructField{cap: 20}
|
||||
mut methods := []ast.FnDecl{cap: 20}
|
||||
mut embeds := []ast.InterfaceEmbedding{}
|
||||
mut is_mut := false
|
||||
mut mut_pos := -1
|
||||
mut ifaces := []ast.InterfaceEmbedding{}
|
||||
for p.tok.kind != .rcbr && p.tok.kind != .eof {
|
||||
if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital()
|
||||
&& (p.peek_tok.line_nr != p.tok.line_nr
|
||||
|
|
@ -526,7 +532,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
iface_name = p.table.sym(iface_type).name
|
||||
}
|
||||
comments := p.eat_comments()
|
||||
ifaces << ast.InterfaceEmbedding{
|
||||
embeds << ast.InterfaceEmbedding{
|
||||
name: iface_name
|
||||
typ: iface_type
|
||||
pos: iface_pos
|
||||
|
|
@ -552,7 +558,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
break
|
||||
}
|
||||
comments := p.eat_comments()
|
||||
ifaces << ast.InterfaceEmbedding{
|
||||
embeds << ast.InterfaceEmbedding{
|
||||
name: from_mod_name
|
||||
typ: from_mod_typ
|
||||
pos: p.prev_tok.pos()
|
||||
|
|
@ -663,7 +669,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
}
|
||||
}
|
||||
}
|
||||
info.embeds = ifaces.map(it.typ)
|
||||
info.embeds = embeds.map(it.typ)
|
||||
ts.info = info
|
||||
p.top_level_statement_end()
|
||||
p.check(.rcbr)
|
||||
|
|
@ -674,7 +680,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
typ: typ
|
||||
fields: fields
|
||||
methods: methods
|
||||
embeds: ifaces
|
||||
embeds: embeds
|
||||
is_pub: is_pub
|
||||
attrs: attrs
|
||||
pos: pos
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ const (
|
|||
'cflags', 'path', 'arch']
|
||||
)
|
||||
|
||||
[heap]
|
||||
[heap; minify]
|
||||
pub struct Preferences {
|
||||
pub mut:
|
||||
os OS // the OS to compile for
|
||||
|
|
@ -99,25 +99,35 @@ pub mut:
|
|||
// verbosity VerboseLevel
|
||||
is_verbose bool
|
||||
// nofmt bool // disable vfmt
|
||||
is_test bool // `v test string_test.v`
|
||||
is_script bool // single file mode (`v program.v`), main function can be skipped
|
||||
is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global
|
||||
is_livemain bool // main program that contains live/hot code
|
||||
is_liveshared bool // a shared library, that will be used in a -live main program
|
||||
is_shared bool // an ordinary shared library, -shared, no matter if it is live or not
|
||||
is_o bool // building an .o file
|
||||
is_prof bool // benchmark every function
|
||||
is_glibc bool // if GLIBC will be linked
|
||||
is_musl bool // if MUSL will be linked
|
||||
is_test bool // `v test string_test.v`
|
||||
is_script bool // single file mode (`v program.v`), main function can be skipped
|
||||
is_vsh bool // v script (`file.vsh`) file, the `os` module should be made global
|
||||
is_livemain bool // main program that contains live/hot code
|
||||
is_liveshared bool // a shared library, that will be used in a -live main program
|
||||
is_shared bool // an ordinary shared library, -shared, no matter if it is live or not
|
||||
is_o bool // building an .o file
|
||||
is_prof bool // benchmark every function
|
||||
is_prod bool // use "-O2"
|
||||
is_repl bool
|
||||
is_run bool
|
||||
is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
|
||||
is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly).
|
||||
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
||||
is_fmt bool
|
||||
is_vet bool
|
||||
is_vweb bool // skip _ var warning in templates
|
||||
is_ios_simulator bool
|
||||
is_apk bool // build as Android .apk format
|
||||
is_help bool // -h, -help or --help was passed
|
||||
is_cstrict bool // turn on more C warnings; slightly slower
|
||||
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.
|
||||
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"
|
||||
is_repl bool
|
||||
is_run bool
|
||||
is_debug bool // turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
|
||||
is_vlines bool // turned on by -g (it slows down .tmp.c generation slightly).
|
||||
// Note: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files,
|
||||
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
|
||||
sanitize bool // use Clang's new "-fsanitize" option
|
||||
|
|
@ -131,7 +141,6 @@ pub mut:
|
|||
dump_c_flags string // `-dump-c-flags file.txt` - let V store all C flags, passed to the backend C compiler in `file.txt`, one C flag/value per line.
|
||||
use_cache bool // when set, use cached modules to speed up subsequent compilations, at the cost of slower initial ones (while the modules are cached)
|
||||
retry_compilation bool = true // retry the compilation with another C compiler, if tcc fails.
|
||||
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
||||
// TODO Convert this into a []string
|
||||
cflags string // Additional options which will be passed to the C compiler.
|
||||
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
|
||||
|
|
@ -146,10 +155,8 @@ pub mut:
|
|||
// Disabling `free()` insertion results in better performance in some applications (e.g. compilers)
|
||||
compress bool // when set, use `upx` to compress the generated executable
|
||||
// generating_vh bool
|
||||
no_builtin bool // Skip adding the `builtin` module implicitly. The generated C code may not compile.
|
||||
enable_globals bool // allow __global for low level code
|
||||
is_fmt bool
|
||||
is_vet bool
|
||||
no_builtin bool // Skip adding the `builtin` module implicitly. The generated C code may not compile.
|
||||
enable_globals bool // allow __global for low level code
|
||||
is_bare bool // set by -freestanding
|
||||
bare_builtin_dir string // Set by -bare-builtin-dir xyz/ . The xyz/ module should contain implementations of malloc, memset, etc, that are used by the rest of V's `builtin` module. That option is only useful with -freestanding (i.e. when is_bare is true).
|
||||
no_preludes bool // Prevents V from generating preludes in resulting .c files
|
||||
|
|
@ -162,41 +169,41 @@ pub mut:
|
|||
out_name_c string // full os.real_path to the generated .tmp.c file; set by builder.
|
||||
out_name string
|
||||
path string // Path to file/folder to compile
|
||||
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }`
|
||||
//
|
||||
run_only []string // VTEST_ONLY_FN and -run-only accept comma separated glob patterns.
|
||||
// Only test_ functions that match these patterns will be run. -run-only is valid only for _test.v files.
|
||||
compile_defines []string // just ['vfmt']
|
||||
compile_defines_all []string // contains both: ['vfmt','another']
|
||||
run_args []string // `v run x.v 1 2 3` => `1 2 3`
|
||||
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
||||
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
||||
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
|
||||
skip_warnings bool // like C's "-w", forces warnings to be ignored.
|
||||
warn_impure_v bool // -Wimpure-v, force a warning for JS.fn()/C.fn(), outside of .js.v/.c.v files. TODO: turn to an error by default
|
||||
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
|
||||
fatal_errors bool // unconditionally exit after the first error with exit(1)
|
||||
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
|
||||
no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them)
|
||||
no_std bool // when true, do not pass -std=gnu99(linux)/-std=c99 to the C backend
|
||||
//
|
||||
// -d vfmt and -d another=0 for `$if vfmt { will execute }` and `$if another ? { will NOT get here }`
|
||||
compile_defines []string // just ['vfmt']
|
||||
compile_defines_all []string // contains both: ['vfmt','another']
|
||||
//
|
||||
run_args []string // `v run x.v 1 2 3` => `1 2 3`
|
||||
printfn_list []string // a list of generated function names, whose source should be shown, for debugging
|
||||
print_v_files bool // when true, just print the list of all parsed .v files then stop.
|
||||
skip_running bool // when true, do no try to run the produced file (set by b.cc(), when -o x.c or -o x.js)
|
||||
skip_warnings bool // like C's "-w", forces warnings to be ignored.
|
||||
warn_impure_v bool // -Wimpure-v, force a warning for JS.fn()/C.fn(), outside of .js.v/.c.v files. TODO: turn to an error by default
|
||||
warns_are_errors bool // -W, like C's "-Werror", treat *every* warning is an error
|
||||
fatal_errors bool // unconditionally exit after the first error with exit(1)
|
||||
reuse_tmpc bool // do not use random names for .tmp.c and .tmp.c.rsp files, and do not remove them
|
||||
no_rsp bool // when true, pass C backend options directly on the CLI (do not use `.rsp` files for them, some older C compilers do not support them)
|
||||
no_std bool // when true, do not pass -std=gnu99(linux)/-std=c99 to the C backend
|
||||
//
|
||||
no_parallel bool // do not use threads when compiling; slower, but more portable and sometimes less buggy
|
||||
only_check_syntax bool // when true, just parse the files, then stop, before running checker
|
||||
check_only bool // same as only_check_syntax, but also runs the checker
|
||||
experimental bool // enable experimental features
|
||||
skip_unused bool // skip generating C code for functions, that are not used
|
||||
show_timings bool // show how much time each compiler stage took
|
||||
//
|
||||
use_color ColorOutput // whether the warnings/errors should use ANSI color escapes.
|
||||
no_parallel bool
|
||||
is_vweb bool // skip _ var warning in templates
|
||||
only_check_syntax bool // when true, just parse the files, then stop, before running checker
|
||||
check_only bool // same as only_check_syntax, but also runs the checker
|
||||
experimental bool // enable experimental features
|
||||
skip_unused bool // skip generating C code for functions, that are not used
|
||||
show_timings bool // show how much time each compiler stage took
|
||||
is_ios_simulator bool
|
||||
is_apk bool // build as Android .apk format
|
||||
cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successfull builds.
|
||||
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
|
||||
cleanup_files []string // list of temporary *.tmp.c and *.tmp.c.rsp files. Cleaned up on successfull builds.
|
||||
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
|
||||
cache_manager vcache.CacheManager
|
||||
is_help bool // -h, -help or --help was passed
|
||||
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak, ...
|
||||
is_cstrict bool // turn on more C warnings; slightly slower
|
||||
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
|
||||
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
|
||||
message_limit int = 100 // the maximum amount of warnings/errors/notices that will be accumulated
|
||||
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
|
||||
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
|
||||
// checker settings:
|
||||
checker_match_exhaustive_cutoff_limit int = 12
|
||||
thread_stack_size int = 8388608 // Change with `-thread-stack-size 4194304`. Note: on macos it was 524288, which is too small for more complex programs with many nested callexprs.
|
||||
|
|
@ -206,8 +213,25 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
|||
return parse_args_and_show_errors(known_external_commands, args, false)
|
||||
}
|
||||
|
||||
[if linux]
|
||||
fn detect_musl(mut res Preferences) {
|
||||
res.is_glibc = true
|
||||
res.is_musl = false
|
||||
if os.exists('/etc/alpine-release') {
|
||||
res.is_musl = true
|
||||
res.is_glibc = false
|
||||
return
|
||||
}
|
||||
my_libs := os.walk_ext('/proc/self/map_files/', '').map(os.real_path(it))
|
||||
if my_libs.any(it.contains('musl')) {
|
||||
res.is_musl = true
|
||||
res.is_glibc = false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_args_and_show_errors(known_external_commands []string, args []string, show_output bool) (&Preferences, string) {
|
||||
mut res := &Preferences{}
|
||||
detect_musl(mut res)
|
||||
$if x64 {
|
||||
res.m64 = true // follow V model by default
|
||||
}
|
||||
|
|
@ -412,6 +436,16 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||
'-no-retry-compilation' {
|
||||
res.retry_compilation = false
|
||||
}
|
||||
'-musl' {
|
||||
res.is_musl = true
|
||||
res.is_glibc = false
|
||||
res.build_options << arg
|
||||
}
|
||||
'-glibc' {
|
||||
res.is_musl = false
|
||||
res.is_glibc = true
|
||||
res.build_options << arg
|
||||
}
|
||||
'-no-builtin' {
|
||||
res.no_builtin = true
|
||||
res.build_options << arg
|
||||
|
|
@ -579,6 +613,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
|
|||
'-cc' {
|
||||
res.ccompiler = cmdline.option(current_args, '-cc', 'cc')
|
||||
res.build_options << '$arg "$res.ccompiler"'
|
||||
if res.ccompiler == 'musl-gcc' {
|
||||
res.is_musl = true
|
||||
res.is_glibc = false
|
||||
}
|
||||
i++
|
||||
}
|
||||
'-checker-match-exhaustive-cutoff-limit' {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ const (
|
|||
backslash = `\\`
|
||||
)
|
||||
|
||||
[minify]
|
||||
pub struct Scanner {
|
||||
pub mut:
|
||||
file_path string // '/path/to/file.v'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
fn test_array_with_it() {
|
||||
assert [0, 1, 2, 3, 4, 5]! == [6]int{init: it}
|
||||
a1 := [6]int{init: it}
|
||||
assert a1 == [0, 1, 2, 3, 4, 5]!
|
||||
|
||||
assert [0, 1, 4, 9, 16, 25] == []int{len: 6, init: it * it}
|
||||
a2 := []int{len: 6, init: it * it}
|
||||
assert a2 == [0, 1, 4, 9, 16, 25]
|
||||
|
||||
assert [1, 2, 3, 4, 5] == []int{len: 5, init: it + 1}
|
||||
a3 := []int{len: 5, init: it + 1}
|
||||
assert a3 == [1, 2, 3, 4, 5]
|
||||
|
||||
assert [5, 4, 3, 2, 1] == []int{len: 5, init: 5 - it}
|
||||
a4 := []int{len: 5, init: 5 - it}
|
||||
assert a4 == [5, 4, 3, 2, 1]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import time
|
||||
|
||||
// vtest flaky: true
|
||||
// vtest retry: 3
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
idx atomic int
|
||||
|
|
|
|||
|
|
@ -5,4 +5,14 @@ fn test_fixed_array_in_op() {
|
|||
|
||||
ch := `"`
|
||||
assert ch in [`"`, `'`]!
|
||||
|
||||
fixed_arr := [1, 2, 3]!
|
||||
|
||||
b1 := 2 in fixed_arr
|
||||
println(b1)
|
||||
assert b1
|
||||
|
||||
b2 := 5 !in fixed_arr
|
||||
println(b2)
|
||||
assert b2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
fn test_generic_fn_infer_fn_type_argument() {
|
||||
to_r := fn (x int) rune {
|
||||
return [`😺`, `😸`, `😹`, `😻`, `😾`][x - 1]
|
||||
}
|
||||
to_f64 := fn (x int) f64 {
|
||||
return f64(x) + 0.123
|
||||
}
|
||||
to_s := fn (x int) string {
|
||||
return ['One', 'Two', 'Three', 'Four', 'Five'][x - 1]
|
||||
}
|
||||
|
||||
items := [1, 2, 3, 4, 5]
|
||||
|
||||
ret_r := fmap(to_r, items)
|
||||
println('${ret_r.map(rune(it))}')
|
||||
assert '${ret_r.map(rune(it))}' == '[`😺`, `😸`, `😹`, `😻`, `😾`]'
|
||||
|
||||
// returns random same number for every item in array
|
||||
ret_f64 := fmap(to_f64, items)
|
||||
println(ret_f64)
|
||||
assert ret_f64 == [1.123, 2.123, 3.123, 4.123, 5.123]
|
||||
|
||||
ret_s := fmap(to_s, items)
|
||||
println(ret_s)
|
||||
assert ret_s == ['One', 'Two', 'Three', 'Four', 'Five']
|
||||
}
|
||||
|
||||
// [noah04 #14214] code
|
||||
fn fmap<I, O>(func fn (I) O, list []I) []O {
|
||||
return []O{len: list.len, init: func(list[it])}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
fn get_arr_v1<N, T>(num N, val T) []T {
|
||||
return []T{len: num, init: val}
|
||||
}
|
||||
|
||||
fn get_arr_v2<N, T>(num N, val T) []T {
|
||||
return []T{len: int(num), init: val}
|
||||
}
|
||||
|
||||
fn get_arr_v3<N, T>(num N, val T) []T {
|
||||
tmp := num
|
||||
return []T{len: tmp, init: val}
|
||||
}
|
||||
|
||||
fn get_arr_v4<N, T>(num N, val T) []T {
|
||||
tmp := num + 0
|
||||
return []T{len: tmp, init: val}
|
||||
}
|
||||
|
||||
fn get_arr_v5<N, T>(num N, val T) []T {
|
||||
tmp := 0 + num
|
||||
return []T{len: tmp, init: val}
|
||||
}
|
||||
|
||||
fn test_generic_array_init() {
|
||||
println(get_arr_v1(2, 'hallo v1'))
|
||||
a1 := get_arr_v1(2, 'hallo v1')
|
||||
assert a1 == ['hallo v1', 'hallo v1']
|
||||
|
||||
println(get_arr_v2(2, 'hallo v2'))
|
||||
a2 := get_arr_v2(2, 'hallo v2')
|
||||
assert a2 == ['hallo v2', 'hallo v2']
|
||||
|
||||
println(get_arr_v3(2, 'hallo v3'))
|
||||
a3 := get_arr_v3(2, 'hallo v3')
|
||||
assert a3 == ['hallo v3', 'hallo v3']
|
||||
|
||||
println(get_arr_v4(2, 'hallo v4'))
|
||||
a4 := get_arr_v4(2, 'hallo v4')
|
||||
assert a4 == ['hallo v4', 'hallo v4']
|
||||
|
||||
println(get_arr_v5(2, 'hallo v5'))
|
||||
a5 := get_arr_v5(2, 'hallo v5')
|
||||
assert a5 == ['hallo v5', 'hallo v5']
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
// that can be found in the LICENSE file.
|
||||
module token
|
||||
|
||||
[minify]
|
||||
pub struct Token {
|
||||
pub:
|
||||
kind Kind // the token number/enum; for quick comparisons
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub enum ErrorType {
|
|||
trailing_space
|
||||
}
|
||||
|
||||
[minify]
|
||||
pub struct Error {
|
||||
pub mut:
|
||||
kind ErrorKind [required]
|
||||
|
|
|
|||
Loading…
Reference in New Issue