Skip to content

Commit cd94cff

Browse files
authored
comptime: support type interpolation in the msg argument of $compile_warn(msg) and $compile_error(msg) (#24992)
1 parent 56b51b6 commit cd94cff

File tree

4 files changed

+123
-6
lines changed

4 files changed

+123
-6
lines changed

vlib/v/checker/comptime.v

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ import v.token
99
import v.util
1010
import v.pkgconfig
1111
import v.type_resolver
12+
import strings
1213

1314
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
1415
if node.left !is ast.EmptyExpr {
1516
node.left_type = c.expr(mut node.left)
1617
}
1718
if node.method_name == 'compile_error' {
18-
c.error(node.args_var, node.pos)
19+
c.error(c.comptime_call_msg(node), node.pos)
1920
return ast.void_type
2021
} else if node.method_name == 'compile_warn' {
21-
c.warn(node.args_var, node.pos)
22+
c.warn(c.comptime_call_msg(node), node.pos)
2223
return ast.void_type
2324
}
2425
if node.is_env {
@@ -203,6 +204,16 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
203204
return f.return_type
204205
}
205206

207+
fn (mut c Checker) comptime_call_msg(node ast.ComptimeCall) string {
208+
return if node.args_var.len > 0 {
209+
node.args_var
210+
} else if value := c.eval_comptime_const_expr(node.args[0].expr, -1) {
211+
value.string() or { '' }
212+
} else {
213+
''
214+
}
215+
}
216+
206217
fn (mut c Checker) comptime_selector(mut node ast.ComptimeSelector) ast.Type {
207218
node.left_type = c.expr(mut node.left)
208219
mut expr_type := c.unwrap_generic(c.expr(mut node.field_expr))
@@ -415,6 +426,22 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
415426
ast.StringLiteral {
416427
return util.smart_quote(expr.val, expr.is_raw)
417428
}
429+
ast.StringInterLiteral {
430+
if nlevel < 0 {
431+
mut sb := strings.new_builder(20)
432+
for i, val in expr.vals {
433+
sb.write_string(val)
434+
if e := expr.exprs[i] {
435+
if value := c.eval_comptime_const_expr(e, nlevel + 1) {
436+
sb.write_string(value.string() or { '' })
437+
} else {
438+
c.error('unsupport expr `${e.str()}`', e.pos())
439+
}
440+
}
441+
}
442+
return sb.str()
443+
}
444+
}
418445
ast.CharLiteral {
419446
runes := expr.val.runes()
420447
if runes.len > 0 {
@@ -427,6 +454,28 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
427454
// an existing constant?
428455
return c.eval_comptime_const_expr(expr.obj.expr, nlevel + 1)
429456
}
457+
idx := c.table.cur_fn.generic_names.index(expr.name)
458+
if typ := c.table.cur_concrete_types[idx] {
459+
sym := c.table.sym(typ)
460+
return sym.str()
461+
}
462+
}
463+
ast.SelectorExpr {
464+
if expr.expr is ast.Ident {
465+
idx := c.table.cur_fn.generic_names.index(expr.expr.name)
466+
if typ := c.table.cur_concrete_types[idx] {
467+
sym := c.table.sym(typ)
468+
match expr.field_name {
469+
'name' {
470+
return sym.name
471+
}
472+
'idx' {
473+
return i32(sym.idx)
474+
}
475+
else {}
476+
}
477+
}
478+
}
430479
}
431480
ast.CastExpr {
432481
cast_expr_value := c.eval_comptime_const_expr(expr.expr, nlevel + 1) or { return none }

vlib/v/fmt/fmt.v

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,10 +2285,16 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
22852285
f.write("\$pkgconfig('${node.args_var}')")
22862286
}
22872287
node.method_name in ['compile_error', 'compile_warn'] {
2288-
if node.args_var.contains("'") {
2289-
f.write('\$${node.method_name}("${node.args_var}")')
2288+
if node.args.len == 0 {
2289+
if node.args_var.contains("'") {
2290+
f.write('\$${node.method_name}("${node.args_var}")')
2291+
} else {
2292+
f.write("\$${node.method_name}('${node.args_var}')")
2293+
}
22902294
} else {
2291-
f.write("\$${node.method_name}('${node.args_var}')")
2295+
f.write('\$${node.method_name}(')
2296+
f.expr(node.args[0].expr)
2297+
f.write(')')
22922298
}
22932299
}
22942300
node.method_name == 'd' {

vlib/v/parser/comptime.v

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
159159
is_html := method_name == 'html'
160160
p.check(.lpar)
161161
arg_pos := p.tok.pos()
162-
if method_name in ['env', 'pkgconfig', 'compile_error', 'compile_warn'] {
162+
if method_name in ['env', 'pkgconfig'] {
163163
s := p.tok.lit
164164
p.check(.string)
165165
p.check(.rpar)
@@ -172,6 +172,28 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
172172
env_pos: start_pos
173173
pos: start_pos.extend(p.prev_tok.pos())
174174
}
175+
} else if method_name in ['compile_error', 'compile_warn'] {
176+
mut s := ''
177+
mut args := []ast.CallArg{}
178+
if p.tok.kind == .string && p.peek_tok.kind == .rpar {
179+
s = p.tok.lit
180+
p.check(.string)
181+
} else {
182+
args << ast.CallArg{
183+
expr: p.string_expr()
184+
typ: ast.string_type
185+
ct_expr: true
186+
}
187+
}
188+
p.check(.rpar)
189+
return ast.ComptimeCall{
190+
scope: unsafe { nil }
191+
method_name: method_name
192+
args_var: s
193+
env_pos: start_pos
194+
pos: start_pos.extend(p.prev_tok.pos())
195+
args: args
196+
}
175197
} else if method_name == 'res' {
176198
mut has_args := false
177199
mut type_index := ''
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const msg = 'invalid type'
2+
3+
fn t[T]() int {
4+
$if T is i8 {
5+
assert typeof[T]().name == typeof[i8]().name
6+
$compile_warn('invalid type ${T.name} ${T.idx}')
7+
return 1
8+
}
9+
return 0
10+
}
11+
12+
fn t2[T, R]() int {
13+
$if T is i8 && R is i16 {
14+
assert typeof[T]().name == typeof[i8]().name
15+
assert typeof[R]().name == typeof[i16]().name
16+
$compile_warn('invalid type ${T.name} ${T.idx}, ${R.name} ${R.idx}')
17+
return 1
18+
}
19+
return 0
20+
}
21+
22+
fn t3[T, R, E]() int {
23+
$if T is i8 && R is i16 && E is i32 {
24+
assert typeof[T]().name == typeof[i8]().name
25+
assert typeof[R]().name == typeof[i16]().name
26+
assert typeof[E]().name == typeof[i32]().name
27+
$compile_warn('invalid type ${T.name} ${T.idx}, ${R.name} ${R.idx}, ${E.name} ${E.idx}')
28+
return 1
29+
}
30+
return 0
31+
}
32+
33+
fn test_main() {
34+
assert t[i8]() == 1
35+
assert t2[i8, i16]() == 1
36+
assert t3[i8, i16, i32]() == 1
37+
assert t[i16]() == 0
38+
assert t2[i16, i16]() == 0
39+
assert t3[i16, i16, i16]() == 0
40+
}

0 commit comments

Comments
 (0)