Compare commits

...

24 Commits

Author SHA1 Message Date
yuyi 41857b0acf
checker: check error for fn call argument mismatch (fix #14280) (#14283)
ci/woodpecker/push/vc Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
2022-05-04 11:52:32 +02:00
playX 95d94e683e
checker: allow rune->any int and vice versa when translated (#14285) 2022-05-04 11:52:32 +02:00
Alexander Medvednikov b2b82931d1
checker: temporary c2v const fix 2022-05-04 11:52:32 +02:00
yuyi 010ace62e1
checker, cgen: check comptime selector that has no field name (#14282) 2022-05-04 11:52:32 +02:00
Alexander Medvednikov 6c4f1ce196
checker: c2v rune comparison fix 2022-05-04 11:52:32 +02:00
yuyi 4b48bece20
cgen: cleanup in gen_array_contains_methods() (#14274) 2022-05-04 11:52:32 +02:00
playX ee6d819f22
checker: allow fixed array to pointer and vice versa when translated (#14275) 2022-05-04 11:52:32 +02:00
Delyan Angelov 0badffe651
tests: add `// vtest flaky: true` to atomic_test.v 2022-05-04 11:52:32 +02:00
yuyi f9c4cbdb15
checker: check argument mismatch of array.filter/all/any() (#14273) 2022-05-04 11:52:32 +02:00
Hunam 0669e5952b
doc: add JS backend mention (#14265) 2022-05-04 11:52:31 +02:00
yuyi df0133c89b
cgen: fix error for fixed array in operate (#14269) 2022-05-04 11:52:31 +02:00
Delyan Angelov eb9063bdb7
builtin: improve musl/Alpine support (define weak backtrace/backtrace_symbols/backtrace_symbols_fd symbols) (#14250) 2022-05-04 11:52:31 +02:00
Delyan Angelov e76e0a97e1
vdoc: fix panic on empty `//` comment on `v doc -f html file.v`; turn `expected code block after empty example` to a warning 2022-05-04 11:52:31 +02:00
StunxFS bf8d1f415d
cleanup: delete x.v in project root folder (#14260) 2022-05-04 11:52:31 +02:00
Delyan Angelov cd5c9a83c5
builtin: add missing panic_result_not_set/1 callback function. 2022-05-04 11:52:31 +02:00
spaceface bf10b27a33
checker: refactor comptime_if_branch (#14259) 2022-05-04 11:52:31 +02:00
yuyi 633e612d62
checker: fix a bug in generics array init (#14258) 2022-05-04 11:52:31 +02:00
yuyi 93d49176c7
parser: minor cleanup in interface_decl() (#14257) 2022-05-04 11:52:31 +02:00
yuyi 374b6927bc
checker: fix generic fn infering fn type argument (fix #14243) (#14256) 2022-05-04 11:52:31 +02:00
yuyi 21555fb03a
parser: fix inline array's element access (#14253) 2022-05-04 11:52:30 +02:00
playX 6468bcd0a6
cgen: fix const decl gen when translated (#14255) 2022-05-04 11:52:30 +02:00
Isaiah 9a1d73f622
builder: allow `-compress` to work on windows too, when `upx` is installed (#14252)
Allow `-compress` flag on Windows if upx is installed. If upx not installed, same behavior as on linux/mac
2022-05-04 11:52:30 +02:00
yuyi 8d1cc6a817
cgen: fix fixed array init with `it` (#14251) 2022-05-04 11:52:30 +02:00
spaceface bf86fff9cc
checker, gen: add support for a [minify] struct attribute (#14247) 2022-05-04 11:52:30 +02:00
64 changed files with 1035 additions and 279 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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_

View File

@ -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
}

View File

@ -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]

View File

@ -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) {
}

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -14,6 +14,7 @@ pub enum AttrKind {
}
// e.g. `[unsafe]`
[minify]
pub struct Attr {
pub:
name string // [name]

View File

@ -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

View File

@ -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')
}

View File

@ -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

View File

@ -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')
}
}
}

View File

@ -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{}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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 {}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 | }

View File

@ -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)
}

View File

@ -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 | }

View File

@ -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() )
}
}
}

View File

@ -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)

View File

@ -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 |

View File

@ -0,0 +1,7 @@
fn main() {
foo(bar())
}
fn foo(x int) {}
fn bar() {}

View File

@ -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

View File

@ -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() {

View File

@ -10,6 +10,7 @@ pub enum Reporter {
gen
}
[minify]
pub struct Error {
pub:
message string

View File

@ -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

View File

@ -15,6 +15,7 @@ const (
max_len = [0, 35, 60, 85, 93, 100]
)
[minify]
pub struct Fmt {
pub mut:
file ast.File

View 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}')

View File

@ -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('))')
}

View File

@ -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;')

View File

@ -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

View File

@ -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
}
}

View File

@ -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*.

View File

@ -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 {

View File

@ -0,0 +1,5 @@
#if defined(__GLIBC__)
x = 2;
#else
x = 3;
#endif

View File

@ -0,0 +1,9 @@
fn main() {
mut x := 1
$if glibc {
x = 2
} $else {
x = 3
}
println('done')
}

View File

@ -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()));

View File

@ -0,0 +1,2 @@
42
11

View File

@ -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())
}

View File

@ -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

View File

@ -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

View File

@ -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' {

View File

@ -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
})

View File

@ -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

View File

@ -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' {

View File

@ -23,6 +23,7 @@ const (
backslash = `\\`
)
[minify]
pub struct Scanner {
pub mut:
file_path string // '/path/to/file.v'

View File

@ -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]
}

View File

@ -1,5 +1,8 @@
import time
// vtest flaky: true
// vtest retry: 3
struct App {
mut:
idx atomic int

View File

@ -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
}

View File

@ -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])}
}

View File

@ -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']
}

View File

@ -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

View File

@ -22,6 +22,7 @@ pub enum ErrorType {
trailing_space
}
[minify]
pub struct Error {
pub mut:
kind ErrorKind [required]