all: initial implementation of compile-time types (#13549)
parent
83cdd8bedd
commit
b842e89acc
|
@ -24,6 +24,7 @@ pub type Expr = AnonFn
|
||||||
| Comment
|
| Comment
|
||||||
| ComptimeCall
|
| ComptimeCall
|
||||||
| ComptimeSelector
|
| ComptimeSelector
|
||||||
|
| ComptimeType
|
||||||
| ConcatExpr
|
| ConcatExpr
|
||||||
| DumpExpr
|
| DumpExpr
|
||||||
| EmptyExpr
|
| EmptyExpr
|
||||||
|
@ -114,6 +115,36 @@ pub:
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ComptimeTypeKind {
|
||||||
|
map_
|
||||||
|
int
|
||||||
|
float
|
||||||
|
struct_
|
||||||
|
iface
|
||||||
|
array
|
||||||
|
sum_type
|
||||||
|
enum_
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ComptimeType {
|
||||||
|
pub:
|
||||||
|
kind ComptimeTypeKind
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (cty ComptimeType) str() string {
|
||||||
|
return match cty.kind {
|
||||||
|
.map_ { '\$Map' }
|
||||||
|
.int { '\$Int' }
|
||||||
|
.float { '\$Float' }
|
||||||
|
.struct_ { '\$Struct' }
|
||||||
|
.iface { '\$Interface' }
|
||||||
|
.array { '\$Array' }
|
||||||
|
.sum_type { '\$Sumtype' }
|
||||||
|
.enum_ { '\$Enum' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EmptyExpr {
|
pub struct EmptyExpr {
|
||||||
x int
|
x int
|
||||||
}
|
}
|
||||||
|
@ -1683,7 +1714,7 @@ pub fn (expr Expr) pos() token.Pos {
|
||||||
EnumVal, DumpExpr, FloatLiteral, GoExpr, Ident, IfExpr, IntegerLiteral, IsRefType, Likely,
|
EnumVal, DumpExpr, FloatLiteral, GoExpr, Ident, IfExpr, IntegerLiteral, IsRefType, Likely,
|
||||||
LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr,
|
LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr,
|
||||||
RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral,
|
RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral,
|
||||||
StructInit, TypeNode, TypeOf, UnsafeExpr {
|
StructInit, TypeNode, TypeOf, UnsafeExpr, ComptimeType {
|
||||||
return expr.pos
|
return expr.pos
|
||||||
}
|
}
|
||||||
IndexExpr {
|
IndexExpr {
|
||||||
|
|
|
@ -261,6 +261,9 @@ pub fn (x Expr) str() string {
|
||||||
AnonFn {
|
AnonFn {
|
||||||
return 'anon_fn'
|
return 'anon_fn'
|
||||||
}
|
}
|
||||||
|
ComptimeType {
|
||||||
|
return x.str()
|
||||||
|
}
|
||||||
DumpExpr {
|
DumpExpr {
|
||||||
return 'dump($x.expr.str())'
|
return 'dump($x.expr.str())'
|
||||||
}
|
}
|
||||||
|
|
|
@ -2044,3 +2044,49 @@ pub fn (mut t Table) generic_insts_to_concrete() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (t &Table) is_comptime_type(x Type, y ComptimeType) bool {
|
||||||
|
x_kind := t.type_kind(x)
|
||||||
|
match y.kind {
|
||||||
|
.map_ {
|
||||||
|
return x_kind == .map
|
||||||
|
}
|
||||||
|
.int {
|
||||||
|
return x_kind in [
|
||||||
|
.i8,
|
||||||
|
.i16,
|
||||||
|
.int,
|
||||||
|
.i64,
|
||||||
|
.byte,
|
||||||
|
.u8,
|
||||||
|
.u16,
|
||||||
|
.u32,
|
||||||
|
.u64,
|
||||||
|
.usize,
|
||||||
|
.int_literal,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
.float {
|
||||||
|
return x_kind in [
|
||||||
|
.f32,
|
||||||
|
.f64,
|
||||||
|
.float_literal,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
.struct_ {
|
||||||
|
return x_kind == .struct_
|
||||||
|
}
|
||||||
|
.iface {
|
||||||
|
return x_kind == .interface_
|
||||||
|
}
|
||||||
|
.array {
|
||||||
|
return x_kind in [.array, .array_fixed]
|
||||||
|
}
|
||||||
|
.sum_type {
|
||||||
|
return x_kind == .sum_type
|
||||||
|
}
|
||||||
|
.enum_ {
|
||||||
|
return x_kind == .enum_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2384,6 +2384,9 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type {
|
||||||
}
|
}
|
||||||
match mut node {
|
match mut node {
|
||||||
ast.NodeError {}
|
ast.NodeError {}
|
||||||
|
ast.ComptimeType {
|
||||||
|
c.error('incorrect use of compile-time type', node.pos)
|
||||||
|
}
|
||||||
ast.EmptyExpr {
|
ast.EmptyExpr {
|
||||||
c.error('checker.expr(): unhandled EmptyExpr', token.Pos{})
|
c.error('checker.expr(): unhandled EmptyExpr', token.Pos{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,6 +468,10 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) bool {
|
||||||
// c.error('`$sym.name` is not an interface', cond.right.pos())
|
// c.error('`$sym.name` is not an interface', cond.right.pos())
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
} 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)
|
||||||
} else if cond.left in [ast.SelectorExpr, ast.TypeNode] {
|
} else if cond.left in [ast.SelectorExpr, ast.TypeNode] {
|
||||||
// `$if method.@type is string`
|
// `$if method.@type is string`
|
||||||
c.expr(cond.left)
|
c.expr(cond.left)
|
||||||
|
|
|
@ -52,32 +52,39 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
||||||
mut comptime_field_name := ''
|
mut comptime_field_name := ''
|
||||||
if mut branch.cond is ast.InfixExpr {
|
if mut branch.cond is ast.InfixExpr {
|
||||||
if branch.cond.op == .key_is {
|
if branch.cond.op == .key_is {
|
||||||
if branch.cond.right !is ast.TypeNode {
|
if branch.cond.right !is ast.TypeNode && branch.cond.right !is ast.ComptimeType {
|
||||||
c.error('invalid `\$if` condition: expected a type', branch.cond.right.pos())
|
c.error('invalid `\$if` condition: expected a type', branch.cond.right.pos())
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
got_type := c.unwrap_generic((branch.cond.right as ast.TypeNode).typ)
|
|
||||||
sym := c.table.sym(got_type)
|
|
||||||
if sym.kind == .placeholder || got_type.has_flag(.generic) {
|
|
||||||
c.error('unknown type `$sym.name`', branch.cond.right.pos())
|
|
||||||
}
|
|
||||||
left := branch.cond.left
|
left := branch.cond.left
|
||||||
if left is ast.SelectorExpr {
|
if branch.cond.right is ast.ComptimeType && left is ast.TypeNode {
|
||||||
comptime_field_name = left.expr.str()
|
|
||||||
c.comptime_fields_type[comptime_field_name] = got_type
|
|
||||||
is_comptime_type_is_expr = true
|
is_comptime_type_is_expr = true
|
||||||
} else if branch.cond.right is ast.TypeNode && left is ast.TypeNode
|
|
||||||
&& sym.kind == .interface_ {
|
|
||||||
is_comptime_type_is_expr = true
|
|
||||||
// is interface
|
|
||||||
checked_type := c.unwrap_generic(left.typ)
|
checked_type := c.unwrap_generic(left.typ)
|
||||||
should_skip = !c.table.does_type_implement_interface(checked_type,
|
should_skip = !c.table.is_comptime_type(checked_type, branch.cond.right as ast.ComptimeType)
|
||||||
got_type)
|
} else {
|
||||||
} else if left is ast.TypeNode {
|
got_type := c.unwrap_generic((branch.cond.right as ast.TypeNode).typ)
|
||||||
is_comptime_type_is_expr = true
|
sym := c.table.sym(got_type)
|
||||||
left_type := c.unwrap_generic(left.typ)
|
if sym.kind == .placeholder || got_type.has_flag(.generic) {
|
||||||
if left_type != got_type {
|
c.error('unknown type `$sym.name`', branch.cond.right.pos())
|
||||||
should_skip = true
|
}
|
||||||
|
|
||||||
|
if left is ast.SelectorExpr {
|
||||||
|
comptime_field_name = left.expr.str()
|
||||||
|
c.comptime_fields_type[comptime_field_name] = got_type
|
||||||
|
is_comptime_type_is_expr = true
|
||||||
|
} else if branch.cond.right is ast.TypeNode && left is ast.TypeNode
|
||||||
|
&& sym.kind == .interface_ {
|
||||||
|
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,
|
||||||
|
got_type)
|
||||||
|
} 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -660,6 +660,18 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||||
ast.UnsafeExpr {
|
ast.UnsafeExpr {
|
||||||
f.unsafe_expr(node)
|
f.unsafe_expr(node)
|
||||||
}
|
}
|
||||||
|
ast.ComptimeType {
|
||||||
|
match node.kind {
|
||||||
|
.array { f.write('\$Array') }
|
||||||
|
.struct_ { f.write('\$Struct') }
|
||||||
|
.iface { f.write('\$Interface') }
|
||||||
|
.map_ { f.write('\$Map') }
|
||||||
|
.int { f.write('\$Int') }
|
||||||
|
.float { f.write('\$Float') }
|
||||||
|
.sum_type { f.write('\$Sumtype') }
|
||||||
|
.enum_ { f.write('\$Enum') }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2740,6 +2740,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
}
|
}
|
||||||
// NB: please keep the type names in the match here in alphabetical order:
|
// NB: please keep the type names in the match here in alphabetical order:
|
||||||
match mut node {
|
match mut node {
|
||||||
|
ast.ComptimeType {
|
||||||
|
g.error('g.expr(): Unhandled ComptimeType', node.pos)
|
||||||
|
}
|
||||||
ast.EmptyExpr {
|
ast.EmptyExpr {
|
||||||
g.error('g.expr(): unhandled EmptyExpr', token.Pos{})
|
g.error('g.expr(): unhandled EmptyExpr', token.Pos{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,6 +337,25 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool {
|
||||||
.key_is, .not_is {
|
.key_is, .not_is {
|
||||||
left := cond.left
|
left := cond.left
|
||||||
mut name := ''
|
mut name := ''
|
||||||
|
if left is ast.TypeNode && cond.right is ast.ComptimeType {
|
||||||
|
checked_type := g.unwrap_generic(left.typ)
|
||||||
|
is_true := g.table.is_comptime_type(checked_type, cond.right)
|
||||||
|
if cond.op == .key_is {
|
||||||
|
if is_true {
|
||||||
|
g.write('1')
|
||||||
|
} else {
|
||||||
|
g.write('0')
|
||||||
|
}
|
||||||
|
return is_true
|
||||||
|
} else {
|
||||||
|
if is_true {
|
||||||
|
g.write('0')
|
||||||
|
} else {
|
||||||
|
g.write('1')
|
||||||
|
}
|
||||||
|
return !is_true
|
||||||
|
}
|
||||||
|
}
|
||||||
mut exp_type := ast.Type(0)
|
mut exp_type := ast.Type(0)
|
||||||
got_type := (cond.right as ast.TypeNode).typ
|
got_type := (cond.right as ast.TypeNode).typ
|
||||||
// Handle `$if x is Interface {`
|
// Handle `$if x is Interface {`
|
||||||
|
|
|
@ -856,6 +856,9 @@ fn (mut g JsGen) stmt(node ast.Stmt) {
|
||||||
fn (mut g JsGen) expr(node ast.Expr) {
|
fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
// NB: please keep the type names in the match here in alphabetical order:
|
// NB: please keep the type names in the match here in alphabetical order:
|
||||||
match mut node {
|
match mut node {
|
||||||
|
ast.ComptimeType {
|
||||||
|
verror('not yet implemented')
|
||||||
|
}
|
||||||
ast.EmptyExpr {}
|
ast.EmptyExpr {}
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
g.gen_anon_fn(mut node)
|
g.gen_anon_fn(mut node)
|
||||||
|
|
|
@ -221,6 +221,7 @@ fn (mut w Walker) expr(node ast.Expr) {
|
||||||
// TODO make sure this doesn't happen
|
// TODO make sure this doesn't happen
|
||||||
// panic('Walker: EmptyExpr')
|
// panic('Walker: EmptyExpr')
|
||||||
}
|
}
|
||||||
|
ast.ComptimeType {}
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
w.fn_decl(mut node.decl)
|
w.fn_decl(mut node.decl)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,50 @@ import v.token
|
||||||
|
|
||||||
const (
|
const (
|
||||||
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig']
|
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig']
|
||||||
|
comptime_types = ['Map', 'Array', 'Int', 'Float', 'Struct', 'Interface', 'Enum',
|
||||||
|
'Sumtype']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
pub fn (mut p Parser) parse_comptime_type() ast.ComptimeType {
|
||||||
|
mut node := ast.ComptimeType{ast.ComptimeTypeKind.map_, p.tok.pos()}
|
||||||
|
|
||||||
|
p.check(.dollar)
|
||||||
|
name := p.check_name()
|
||||||
|
if name !in parser.comptime_types {
|
||||||
|
p.error('unsupported compile-time type `$name`: only $parser.comptime_types are supported')
|
||||||
|
}
|
||||||
|
mut cty := ast.ComptimeTypeKind.map_
|
||||||
|
match name {
|
||||||
|
'Map' {
|
||||||
|
cty = .map_
|
||||||
|
}
|
||||||
|
'Struct' {
|
||||||
|
cty = .struct_
|
||||||
|
}
|
||||||
|
'Interface' {
|
||||||
|
cty = .iface
|
||||||
|
}
|
||||||
|
'Int' {
|
||||||
|
cty = .int
|
||||||
|
}
|
||||||
|
'Float' {
|
||||||
|
cty = .float
|
||||||
|
}
|
||||||
|
'Array' {
|
||||||
|
cty = .array
|
||||||
|
}
|
||||||
|
'Enum' {
|
||||||
|
cty = .enum_
|
||||||
|
}
|
||||||
|
'Sumtype' {
|
||||||
|
cty = .sum_type
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
node = ast.ComptimeType{cty, node.pos}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
// // #include, #flag, #v
|
// // #include, #flag, #v
|
||||||
fn (mut p Parser) hash() ast.HashStmt {
|
fn (mut p Parser) hash() ast.HashStmt {
|
||||||
pos := p.tok.pos()
|
pos := p.tok.pos()
|
||||||
|
|
|
@ -78,7 +78,11 @@ pub fn (mut p Parser) check_expr(precedence int) ?ast.Expr {
|
||||||
.dollar {
|
.dollar {
|
||||||
match p.peek_tok.kind {
|
match p.peek_tok.kind {
|
||||||
.name {
|
.name {
|
||||||
node = p.comptime_call()
|
if p.peek_tok.lit in comptime_types {
|
||||||
|
node = p.parse_comptime_type()
|
||||||
|
} else {
|
||||||
|
node = p.comptime_call()
|
||||||
|
}
|
||||||
p.is_stmt_ident = is_stmt_ident
|
p.is_stmt_ident = is_stmt_ident
|
||||||
}
|
}
|
||||||
.key_if {
|
.key_if {
|
||||||
|
@ -490,6 +494,7 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
|
||||||
if is_key_in {
|
if is_key_in {
|
||||||
p.inside_in_array = true
|
p.inside_in_array = true
|
||||||
}
|
}
|
||||||
|
|
||||||
right = p.expr(precedence)
|
right = p.expr(precedence)
|
||||||
if is_key_in {
|
if is_key_in {
|
||||||
p.inside_in_array = false
|
p.inside_in_array = false
|
||||||
|
|
|
@ -2105,6 +2105,11 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||||
prev_tok_kind := p.prev_tok.kind
|
prev_tok_kind := p.prev_tok.kind
|
||||||
mut node := ast.empty_expr()
|
mut node := ast.empty_expr()
|
||||||
if p.expecting_type {
|
if p.expecting_type {
|
||||||
|
if p.tok.kind == .dollar {
|
||||||
|
node = p.parse_comptime_type()
|
||||||
|
p.expecting_type = false
|
||||||
|
return node
|
||||||
|
}
|
||||||
p.expecting_type = false
|
p.expecting_type = false
|
||||||
// get type position before moving to next
|
// get type position before moving to next
|
||||||
type_pos := p.tok.pos()
|
type_pos := p.tok.pos()
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
fn assert_map<T>() {
|
||||||
|
$if T is $Map {
|
||||||
|
assert true
|
||||||
|
} $else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_array<T>() {
|
||||||
|
$if T is $Array {
|
||||||
|
assert true
|
||||||
|
} $else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_struct<T>() {
|
||||||
|
$if T is $Struct {
|
||||||
|
assert true
|
||||||
|
} $else {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_not_struct<T>() {
|
||||||
|
$if T is $Struct {
|
||||||
|
assert false
|
||||||
|
} $else {
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_not_map<T>() {
|
||||||
|
$if T is $Map {
|
||||||
|
assert false
|
||||||
|
} $else {
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_not_array<T>() {
|
||||||
|
$if T is $Array {
|
||||||
|
assert false
|
||||||
|
} $else {
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Abc {}
|
||||||
|
|
||||||
|
struct Bc {}
|
||||||
|
|
||||||
|
struct Cd {}
|
||||||
|
|
||||||
|
fn test_kind_map() {
|
||||||
|
assert_map<map[int]int>()
|
||||||
|
assert_map<map[string]int>()
|
||||||
|
assert_map<map[i64]i8>()
|
||||||
|
|
||||||
|
assert_not_map<Abc>()
|
||||||
|
assert_not_map<int>()
|
||||||
|
assert_not_map<[]int>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_kind_array() {
|
||||||
|
assert_array<[]int>()
|
||||||
|
assert_array<[]f32>()
|
||||||
|
assert_array<[]string>()
|
||||||
|
|
||||||
|
assert_not_array<Abc>()
|
||||||
|
assert_not_array<string>()
|
||||||
|
assert_not_array<int>()
|
||||||
|
assert_not_array<map[int]int>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_kind_struct() {
|
||||||
|
assert_struct<Abc>()
|
||||||
|
assert_struct<Bc>()
|
||||||
|
assert_struct<Cd>()
|
||||||
|
|
||||||
|
assert_not_struct<int>()
|
||||||
|
assert_not_struct<[]int>()
|
||||||
|
assert_not_struct<map[int]int>()
|
||||||
|
}
|
Loading…
Reference in New Issue