parent
d0c961ebc0
commit
0386f2bbea
|
@ -20,7 +20,8 @@ jobs:
|
|||
- name: Build V
|
||||
run: make
|
||||
- name: Clone current Vinix
|
||||
run: git clone https://github.com/vlang/vinix.git --depth=1
|
||||
run: |
|
||||
git clone https://github.com/vlang/vinix.git --depth 1
|
||||
- name: Clone current mlibc
|
||||
run: git clone https://github.com/managarm/mlibc.git --depth=1
|
||||
- name: Patch mlibc for Vinix
|
||||
|
|
|
@ -39,9 +39,9 @@ fn example_with_cancel() {
|
|||
// The callers of gen need to cancel the context once
|
||||
// they are done consuming generated integers not to leak
|
||||
// the internal routine started by gen.
|
||||
gen := fn (ctx context.Context) chan int {
|
||||
gen := fn (mut ctx context.Context) chan int {
|
||||
dst := chan int{}
|
||||
go fn (ctx context.Context, dst chan int) {
|
||||
go fn (mut ctx context.Context, dst chan int) {
|
||||
mut v := 0
|
||||
ch := ctx.done()
|
||||
for {
|
||||
|
@ -55,16 +55,20 @@ fn example_with_cancel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}(ctx, dst)
|
||||
}(mut ctx, dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
ctx, cancel := context.with_cancel(context.background())
|
||||
mut background := context.background()
|
||||
mut b := &background
|
||||
mut ctx, cancel := context.with_cancel(mut b)
|
||||
defer {
|
||||
cancel()
|
||||
}
|
||||
|
||||
ch := gen(ctx)
|
||||
mut mut_ctx := ctx
|
||||
mut ctx2 := &mut_ctx
|
||||
ch := gen(mut ctx2)
|
||||
for i in 0 .. 5 {
|
||||
v := <-ch
|
||||
assert i == v
|
||||
|
@ -87,7 +91,9 @@ const (
|
|||
// function that it should abandon its work as soon as it gets to it.
|
||||
fn example_with_deadline() {
|
||||
dur := time.now().add(short_duration)
|
||||
ctx, cancel := context.with_deadline(context.background(), dur)
|
||||
mut background := context.background()
|
||||
mut b := &background
|
||||
mut ctx, cancel := context.with_deadline(mut b, dur)
|
||||
|
||||
defer {
|
||||
// Even though ctx will be expired, it is good practice to call its
|
||||
|
@ -122,7 +128,9 @@ const (
|
|||
fn example_with_timeout() {
|
||||
// Pass a context with a timeout to tell a blocking function that it
|
||||
// should abandon its work after the timeout elapses.
|
||||
ctx, cancel := context.with_timeout(context.background(), short_duration)
|
||||
mut background := context.background()
|
||||
mut b := &background
|
||||
mut ctx, cancel := context.with_timeout(mut b, short_duration)
|
||||
defer {
|
||||
cancel()
|
||||
}
|
||||
|
|
|
@ -43,23 +43,6 @@ pub interface Context {
|
|||
// should be canceled. deadline returns none when no deadline is
|
||||
// set. Successive calls to deadline return the same results.
|
||||
deadline() ?time.Time
|
||||
// done returns a channel that's closed when work done on behalf of this
|
||||
// context should be canceled. done may return nil if this context can
|
||||
// never be canceled. Successive calls to done return the same value.
|
||||
// The close of the done channel may happen asynchronously,
|
||||
// after the cancel function returns.
|
||||
//
|
||||
// with_cancel arranges for done to be closed when cancel is called;
|
||||
// with_deadline arranges for done to be closed when the deadline
|
||||
// expires; with_timeout arranges for done to be closed when the timeout
|
||||
// elapses.
|
||||
done() chan int
|
||||
// If done is not yet closed, err returns nil.
|
||||
// If done is closed, err returns a non-nil error explaining why:
|
||||
// canceled if the context was canceled
|
||||
// or deadline_exceeded if the context's deadline passed.
|
||||
// After err returns a non-nil error, successive calls to err return the same error.
|
||||
err() IError
|
||||
// Value returns the value associated with this context for key, or nil
|
||||
// if no value is associated with key. Successive calls to Value with
|
||||
// the same key returns the same result.
|
||||
|
@ -76,6 +59,24 @@ pub interface Context {
|
|||
// collisions.
|
||||
value(key Key) ?Any
|
||||
str() string
|
||||
// done returns a channel that's closed when work done on behalf of this
|
||||
// context should be canceled. done may return nil if this context can
|
||||
// never be canceled. Successive calls to done return the same value.
|
||||
// The close of the done channel may happen asynchronously,
|
||||
// after the cancel function returns.
|
||||
//
|
||||
// with_cancel arranges for done to be closed when cancel is called;
|
||||
// with_deadline arranges for done to be closed when the deadline
|
||||
// expires; with_timeout arranges for done to be closed when the timeout
|
||||
// elapses.
|
||||
mut:
|
||||
done() chan int
|
||||
// If done is not yet closed, err returns nil.
|
||||
// If done is closed, err returns a non-nil error explaining why:
|
||||
// canceled if the context was canceled
|
||||
// or deadline_exceeded if the context's deadline passed.
|
||||
// After err returns a non-nil error, successive calls to err return the same error.
|
||||
err() IError
|
||||
}
|
||||
|
||||
// background returns an empty Context. It is never canceled, has no
|
||||
|
|
|
@ -12,12 +12,13 @@ pub type CancelFn = fn ()
|
|||
|
||||
pub interface Canceler {
|
||||
id string
|
||||
mut:
|
||||
cancel(remove_from_parent bool, err IError)
|
||||
done() chan int
|
||||
}
|
||||
|
||||
[deprecated]
|
||||
pub fn cancel(ctx Context) {
|
||||
pub fn cancel(mut ctx Context) {
|
||||
match mut ctx {
|
||||
CancelContext {
|
||||
ctx.cancel(true, canceled)
|
||||
|
@ -47,9 +48,9 @@ mut:
|
|||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
pub fn with_cancel(parent Context) (Context, CancelFn) {
|
||||
pub fn with_cancel(mut parent Context) (Context, CancelFn) {
|
||||
mut c := new_cancel_context(parent)
|
||||
propagate_cancel(parent, c)
|
||||
propagate_cancel(mut parent, mut c)
|
||||
return Context(c), fn [mut c] () {
|
||||
c.cancel(true, canceled)
|
||||
}
|
||||
|
@ -116,18 +117,20 @@ fn (mut ctx CancelContext) cancel(remove_from_parent bool, err IError) {
|
|||
|
||||
for _, child in ctx.children {
|
||||
// NOTE: acquiring the child's lock while holding parent's lock.
|
||||
child.cancel(false, err)
|
||||
mut c := child
|
||||
c.cancel(false, err)
|
||||
}
|
||||
|
||||
ctx.children = map[string]Canceler{}
|
||||
ctx.mutex.unlock()
|
||||
|
||||
if remove_from_parent {
|
||||
remove_child(ctx.context, ctx)
|
||||
mut cc := &ctx.context
|
||||
remove_child(mut cc, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
fn propagate_cancel(parent Context, child Canceler) {
|
||||
fn propagate_cancel(mut parent Context, mut child Canceler) {
|
||||
done := parent.done()
|
||||
select {
|
||||
_ := <-done {
|
||||
|
@ -136,15 +139,15 @@ fn propagate_cancel(parent Context, child Canceler) {
|
|||
return
|
||||
}
|
||||
}
|
||||
mut p := parent_cancel_context(parent) or {
|
||||
go fn (parent Context, child Canceler) {
|
||||
mut p := parent_cancel_context(mut parent) or {
|
||||
go fn (mut parent Context, mut child Canceler) {
|
||||
pdone := parent.done()
|
||||
select {
|
||||
_ := <-pdone {
|
||||
child.cancel(false, parent.err())
|
||||
}
|
||||
}
|
||||
}(parent, child)
|
||||
}(mut parent, mut child)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -162,7 +165,7 @@ fn propagate_cancel(parent Context, child Canceler) {
|
|||
// parent.done() matches that CancelContext. (If not, the CancelContext
|
||||
// has been wrapped in a custom implementation providing a
|
||||
// different done channel, in which case we should not bypass it.)
|
||||
fn parent_cancel_context(parent Context) ?&CancelContext {
|
||||
fn parent_cancel_context(mut parent Context) ?&CancelContext {
|
||||
done := parent.done()
|
||||
if done.closed {
|
||||
return none
|
||||
|
@ -181,7 +184,7 @@ fn parent_cancel_context(parent Context) ?&CancelContext {
|
|||
}
|
||||
|
||||
// remove_child removes a context from its parent.
|
||||
fn remove_child(parent Context, child Canceler) {
|
||||
mut p := parent_cancel_context(parent) or { return }
|
||||
fn remove_child(mut parent Context, child Canceler) {
|
||||
mut p := parent_cancel_context(mut parent) or { return }
|
||||
p.children.delete(child.id)
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ fn test_with_cancel() {
|
|||
// The callers of gen need to cancel the context once
|
||||
// they are done consuming generated integers not to leak
|
||||
// the internal routine started by gen.
|
||||
gen := fn (ctx context.Context) chan int {
|
||||
gen := fn (mut ctx context.Context) chan int {
|
||||
dst := chan int{}
|
||||
go fn (ctx context.Context, dst chan int) {
|
||||
go fn (mut ctx context.Context, dst chan int) {
|
||||
mut v := 0
|
||||
ch := ctx.done()
|
||||
for {
|
||||
|
@ -26,16 +26,20 @@ fn test_with_cancel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}(ctx, dst)
|
||||
}(mut ctx, dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
ctx, cancel := context.with_cancel(context.background())
|
||||
mut background := context.background()
|
||||
mut b := &background
|
||||
mut ctx, cancel := context.with_cancel(mut b)
|
||||
defer {
|
||||
cancel()
|
||||
}
|
||||
|
||||
ch := gen(ctx)
|
||||
mut mut_ctx := ctx
|
||||
mut ctx2 := &mut_ctx
|
||||
ch := gen(mut ctx2)
|
||||
for i in 0 .. 5 {
|
||||
v := <-ch
|
||||
assert i == v
|
||||
|
|
|
@ -26,12 +26,12 @@ mut:
|
|||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
pub fn with_deadline(parent Context, d time.Time) (Context, CancelFn) {
|
||||
pub fn with_deadline(mut parent Context, d time.Time) (Context, CancelFn) {
|
||||
id := rand.uuid_v4()
|
||||
if cur := parent.deadline() {
|
||||
if cur < d {
|
||||
// The current deadline is already sooner than the new one.
|
||||
return with_cancel(parent)
|
||||
return with_cancel(mut parent)
|
||||
}
|
||||
}
|
||||
cancel_ctx := new_cancel_context(parent)
|
||||
|
@ -40,7 +40,7 @@ pub fn with_deadline(parent Context, d time.Time) (Context, CancelFn) {
|
|||
deadline: d
|
||||
id: id
|
||||
}
|
||||
propagate_cancel(parent, ctx)
|
||||
propagate_cancel(mut parent, mut ctx)
|
||||
dur := d - time.now()
|
||||
if dur.nanoseconds() <= 0 {
|
||||
ctx.cancel(true, deadline_exceeded) // deadline has already passed
|
||||
|
@ -64,8 +64,8 @@ pub fn with_deadline(parent Context, d time.Time) (Context, CancelFn) {
|
|||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete
|
||||
pub fn with_timeout(parent Context, timeout time.Duration) (Context, CancelFn) {
|
||||
return with_deadline(parent, time.now().add(timeout))
|
||||
pub fn with_timeout(mut parent Context, timeout time.Duration) (Context, CancelFn) {
|
||||
return with_deadline(mut parent, time.now().add(timeout))
|
||||
}
|
||||
|
||||
pub fn (ctx &TimerContext) deadline() ?time.Time {
|
||||
|
@ -88,7 +88,8 @@ pub fn (mut ctx TimerContext) cancel(remove_from_parent bool, err IError) {
|
|||
ctx.cancel_ctx.cancel(false, err)
|
||||
if remove_from_parent {
|
||||
// Remove this TimerContext from its parent CancelContext's children.
|
||||
remove_child(ctx.cancel_ctx.context, ctx)
|
||||
mut cc := &ctx.cancel_ctx.context
|
||||
remove_child(mut cc, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ const (
|
|||
// function that it should abandon its work as soon as it gets to it.
|
||||
fn test_with_deadline() {
|
||||
dur := time.now().add(short_duration)
|
||||
ctx, cancel := context.with_deadline(context.background(), dur)
|
||||
mut background := context.background()
|
||||
mut b := &background
|
||||
mut ctx, cancel := context.with_deadline(mut b, dur)
|
||||
|
||||
defer {
|
||||
// Even though ctx will be expired, it is good practice to call its
|
||||
|
@ -33,7 +35,9 @@ fn test_with_deadline() {
|
|||
fn test_with_timeout() {
|
||||
// Pass a context with a timeout to tell a blocking function that it
|
||||
// should abandon its work after the timeout elapses.
|
||||
ctx, cancel := context.with_timeout(context.background(), short_duration)
|
||||
mut background := context.background()
|
||||
mut b := &background
|
||||
mut ctx, cancel := context.with_timeout(mut b, short_duration)
|
||||
defer {
|
||||
cancel()
|
||||
}
|
||||
|
|
|
@ -37,11 +37,11 @@ pub fn (ctx &ValueContext) deadline() ?time.Time {
|
|||
return ctx.context.deadline()
|
||||
}
|
||||
|
||||
pub fn (ctx &ValueContext) done() chan int {
|
||||
pub fn (mut ctx ValueContext) done() chan int {
|
||||
return ctx.context.done()
|
||||
}
|
||||
|
||||
pub fn (ctx &ValueContext) err() IError {
|
||||
pub fn (mut ctx ValueContext) err() IError {
|
||||
return ctx.context.err()
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const (
|
|||
buf_max_len = 1024
|
||||
)
|
||||
|
||||
pub fn cp(src Reader, mut dst Writer) ? {
|
||||
pub fn cp(mut src Reader, mut dst Writer) ? {
|
||||
mut buf := []byte{len: io.buf_max_len}
|
||||
for {
|
||||
len := src.read(mut buf) or { break }
|
||||
|
|
|
@ -8,6 +8,6 @@ fn test_cp() ? {
|
|||
}
|
||||
mut r := io.new_buffered_reader(reader: f)
|
||||
mut stdout := os.stdout()
|
||||
io.cp(r, mut stdout) ?
|
||||
io.cp(mut r, mut stdout) ?
|
||||
assert true
|
||||
}
|
||||
|
|
|
@ -30,12 +30,12 @@ fn (mut w Writ) write(buf []byte) ?int {
|
|||
}
|
||||
|
||||
fn test_copy() {
|
||||
src := Buf{
|
||||
mut src := Buf{
|
||||
bytes: 'abcdefghij'.repeat(10).bytes()
|
||||
}
|
||||
mut dst := Writ{
|
||||
bytes: []byte{}
|
||||
}
|
||||
io.cp(src, mut dst) or { assert false }
|
||||
io.cp(mut src, mut dst) or { assert false }
|
||||
assert dst.bytes == src.bytes
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ pub interface Reader {
|
|||
// them into buf.
|
||||
// A type that implements this should return
|
||||
// `none` on end of stream (EOF) instead of just returning 0
|
||||
mut:
|
||||
read(mut buf []byte) ?int
|
||||
}
|
||||
|
||||
|
@ -17,14 +18,15 @@ const (
|
|||
// ReadAllConfig allows options to be passed for the behaviour
|
||||
// of read_all
|
||||
pub struct ReadAllConfig {
|
||||
reader Reader
|
||||
read_to_end_of_stream bool
|
||||
mut:
|
||||
reader Reader
|
||||
}
|
||||
|
||||
// read_all reads all bytes from a reader until either a 0 length read
|
||||
// or if read_to_end_of_stream is true then the end of the stream (`none`)
|
||||
pub fn read_all(config ReadAllConfig) ?[]byte {
|
||||
r := config.reader
|
||||
mut r := config.reader
|
||||
read_till_eof := config.read_to_end_of_stream
|
||||
|
||||
mut b := []byte{len: io.read_all_len}
|
||||
|
@ -44,7 +46,7 @@ pub fn read_all(config ReadAllConfig) ?[]byte {
|
|||
|
||||
// read_any reads any available bytes from a reader
|
||||
// (until the reader returns a read of 0 length)
|
||||
pub fn read_any(r Reader) ?[]byte {
|
||||
pub fn read_any(mut r Reader) ?[]byte {
|
||||
mut b := []byte{len: io.read_all_len}
|
||||
mut read := 0
|
||||
for {
|
||||
|
|
|
@ -3,6 +3,7 @@ module io
|
|||
// ReaderWriter represents a stream that can be read from and wrote to
|
||||
pub interface ReaderWriter {
|
||||
// from Reader
|
||||
mut:
|
||||
read(mut buf []byte) ?int
|
||||
// from Writer
|
||||
write(buf []byte) ?int
|
||||
|
@ -11,8 +12,8 @@ pub interface ReaderWriter {
|
|||
// ReaderWriterImpl is a ReaderWriter that can be made from
|
||||
// a seperate reader and writer (see fn make_readerwriter)
|
||||
struct ReaderWriterImpl {
|
||||
r Reader
|
||||
mut:
|
||||
r Reader
|
||||
w Writer
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ module io
|
|||
|
||||
// Writer represents a stream of data that can be wrote to
|
||||
pub interface Writer {
|
||||
mut:
|
||||
write(buf []byte) ?int
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ pub enum ServerStatus {
|
|||
}
|
||||
|
||||
interface Handler {
|
||||
mut:
|
||||
handle(Request) Response
|
||||
}
|
||||
|
||||
|
@ -80,7 +81,7 @@ pub fn (s &Server) status() ServerStatus {
|
|||
return s.state
|
||||
}
|
||||
|
||||
fn (s &Server) parse_and_respond(mut conn net.TcpConn) {
|
||||
fn (mut s Server) parse_and_respond(mut conn net.TcpConn) {
|
||||
defer {
|
||||
conn.close() or { eprintln('close() failed: $err') }
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ pub fn (mut c TcpConn) close() ? {
|
|||
c.sock.close() ?
|
||||
}
|
||||
|
||||
pub fn (mut c TcpConn) read_ptr(buf_ptr &byte, len int) ?int {
|
||||
pub fn (c TcpConn) read_ptr(buf_ptr &byte, len int) ?int {
|
||||
mut res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ?
|
||||
$if trace_tcp ? {
|
||||
eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res')
|
||||
|
@ -80,7 +80,7 @@ pub fn (mut c TcpConn) read_ptr(buf_ptr &byte, len int) ?int {
|
|||
return none
|
||||
}
|
||||
|
||||
pub fn (mut c TcpConn) read(mut buf []byte) ?int {
|
||||
pub fn (c TcpConn) read(mut buf []byte) ?int {
|
||||
return c.read_ptr(buf.data, buf.len)
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ pub fn (mut c TcpConn) set_write_timeout(t time.Duration) {
|
|||
}
|
||||
|
||||
[inline]
|
||||
pub fn (mut c TcpConn) wait_for_read() ? {
|
||||
pub fn (c TcpConn) wait_for_read() ? {
|
||||
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import time
|
|||
|
||||
// Backends should provide a `new() ?FdNotifier` function
|
||||
pub interface FdNotifier {
|
||||
mut:
|
||||
add(fd int, events FdEventType, conf ...FdConfigFlags) ?
|
||||
modify(fd int, events FdEventType, conf ...FdConfigFlags) ?
|
||||
remove(fd int) ?
|
||||
|
|
|
@ -27,8 +27,9 @@ fn test_level_trigger() ? {
|
|||
notifier.add(reader, .read) ?
|
||||
|
||||
os.fd_write(writer, 'foobar')
|
||||
check_read_event(notifier, reader, 'foo')
|
||||
check_read_event(notifier, reader, 'bar')
|
||||
mut n := ¬ifier
|
||||
check_read_event(mut n, reader, 'foo')
|
||||
check_read_event(mut n, reader, 'bar')
|
||||
|
||||
assert notifier.wait(0).len == 0
|
||||
}
|
||||
|
@ -46,8 +47,10 @@ fn test_edge_trigger() ? {
|
|||
}
|
||||
notifier.add(reader, .read, .edge_trigger) ?
|
||||
|
||||
mut n := ¬ifier
|
||||
|
||||
os.fd_write(writer, 'foobar')
|
||||
check_read_event(notifier, reader, 'foo')
|
||||
check_read_event(mut n, reader, 'foo')
|
||||
|
||||
assert notifier.wait(0).len == 0
|
||||
|
||||
|
@ -71,15 +74,17 @@ fn test_one_shot() ? {
|
|||
}
|
||||
notifier.add(reader, .read, .one_shot) ?
|
||||
|
||||
mut n := ¬ifier
|
||||
|
||||
os.fd_write(writer, 'foobar')
|
||||
check_read_event(notifier, reader, 'foo')
|
||||
check_read_event(mut n, reader, 'foo')
|
||||
os.fd_write(writer, 'baz')
|
||||
|
||||
assert notifier.wait(0).len == 0
|
||||
|
||||
// rearm
|
||||
notifier.modify(reader, .read) ?
|
||||
check_read_event(notifier, reader, 'barbaz')
|
||||
check_read_event(mut n, reader, 'barbaz')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +153,7 @@ fn test_remove() ? {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_read_event(notifier notify.FdNotifier, reader_fd int, expected string) {
|
||||
fn check_read_event(mut notifier notify.FdNotifier, reader_fd int, expected string) {
|
||||
events := notifier.wait(0)
|
||||
assert events.len == 1
|
||||
assert events[0].fd == reader_fd
|
||||
|
|
|
@ -216,7 +216,7 @@ fn (db DB) handle_error_or_result(res voidptr, elabel string) ?[]Row {
|
|||
|
||||
// copy_expert execute COPY commands
|
||||
// https://www.postgresql.org/docs/9.5/libpq-copy.html
|
||||
pub fn (db DB) copy_expert(query string, file io.ReaderWriter) ?int {
|
||||
pub fn (db DB) copy_expert(query string, mut file io.ReaderWriter) ?int {
|
||||
res := C.PQexec(db.conn, query.str)
|
||||
status := C.PQresultStatus(res)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import rand.wyrand
|
|||
// modules's API. It defines all the methods that a PRNG (in the vlib or custom made) must
|
||||
// implement in order to ensure that _all_ functions can be used with the generator.
|
||||
pub interface PRNG {
|
||||
mut:
|
||||
seed(seed_data []u32)
|
||||
u32() u32
|
||||
u64() u64
|
||||
|
|
|
@ -260,11 +260,23 @@ pub fn (t &Table) is_same_method(f &Fn, func &Fn) string {
|
|||
if f.params.len != func.params.len {
|
||||
return 'expected $f.params.len parameter(s), not $func.params.len'
|
||||
}
|
||||
for i in 1 .. f.params.len {
|
||||
if f.params[i].typ != func.params[i].typ {
|
||||
|
||||
// interface name() other mut name() : error
|
||||
|
||||
for i in 0 .. f.params.len {
|
||||
// don't check receiver for `.typ`
|
||||
has_unexpected_type := i > 0 && f.params[i].typ != func.params[i].typ
|
||||
|
||||
has_unexpected_mutability := !f.params[i].is_mut && func.params[i].is_mut
|
||||
|
||||
if has_unexpected_type || has_unexpected_mutability {
|
||||
exps := t.type_to_str(f.params[i].typ)
|
||||
gots := t.type_to_str(func.params[i].typ)
|
||||
if has_unexpected_type {
|
||||
return 'expected `$exps`, not `$gots` for parameter $i'
|
||||
} else {
|
||||
return 'expected `$exps` which is immutable, not `mut $gots`'
|
||||
}
|
||||
}
|
||||
}
|
||||
return ''
|
||||
|
|
|
@ -4,6 +4,7 @@ import v.ast
|
|||
|
||||
// Visitor defines a visit method which is invoked by the walker in each node it encounters.
|
||||
pub interface Visitor {
|
||||
mut:
|
||||
visit(node &ast.Node) ?
|
||||
}
|
||||
|
||||
|
@ -24,14 +25,15 @@ pub fn (i &Inspector) visit(node &ast.Node) ? {
|
|||
|
||||
// inspect traverses and checks the AST node on a depth-first order and based on the data given
|
||||
pub fn inspect(node &ast.Node, data voidptr, inspector_callback InspectorFn) {
|
||||
walk(Inspector{inspector_callback, data}, node)
|
||||
mut inspector := Inspector{inspector_callback, data}
|
||||
walk(mut inspector, node)
|
||||
}
|
||||
|
||||
// walk traverses the AST using the given visitor
|
||||
pub fn walk(visitor Visitor, node &ast.Node) {
|
||||
pub fn walk(mut visitor Visitor, node &ast.Node) {
|
||||
visitor.visit(node) or { return }
|
||||
children := node.children()
|
||||
for child_node in children {
|
||||
walk(visitor, &child_node)
|
||||
walk(mut visitor, &child_node)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ struct Foo {
|
|||
mut nbo := NodeByOffset{
|
||||
pos: 13
|
||||
}
|
||||
walker.walk(nbo, file)
|
||||
walker.walk(mut nbo, file)
|
||||
assert nbo.node is ast.Stmt
|
||||
stmt := nbo.node as ast.Stmt
|
||||
assert stmt is ast.StructDecl
|
||||
|
|
|
@ -16,7 +16,7 @@ pub fn show(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.File)
|
|||
// Node14 [shape="box",label="PrivateBase",URL="$classPrivateBase.html"];
|
||||
// Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"];
|
||||
for afile in ast_files {
|
||||
walker.walk(mapper, afile)
|
||||
walker.walk(mut mapper, afile)
|
||||
}
|
||||
mapper.dg.finish()
|
||||
}
|
||||
|
|
|
@ -3355,8 +3355,10 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||
}
|
||||
msg := c.table.is_same_method(imethod, method)
|
||||
if msg.len > 0 {
|
||||
sig := c.table.fn_signature(imethod, skip_receiver: true)
|
||||
sig := c.table.fn_signature(imethod, skip_receiver: false)
|
||||
typ_sig := c.table.fn_signature(method, skip_receiver: false)
|
||||
c.add_error_detail('$inter_sym.name has `$sig`')
|
||||
c.add_error_detail(' $typ_sym.name has `$typ_sig`')
|
||||
c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`: $msg',
|
||||
pos)
|
||||
return false
|
||||
|
|
|
@ -4,4 +4,5 @@ vlib/v/checker/tests/unimplemented_interface_b.vv:13:6: error: `Cat` incorrectly
|
|||
13 | foo(c)
|
||||
| ^
|
||||
14 | }
|
||||
Details: main.Animal has `name() string`
|
||||
Details: main.Animal has `fn name(x main.Animal) string`
|
||||
main.Cat has `fn name(c main.Cat)`
|
|
@ -4,4 +4,5 @@ vlib/v/checker/tests/unimplemented_interface_c.vv:12:6: error: `Cat` incorrectly
|
|||
12 | foo(Cat{})
|
||||
| ~~~~~
|
||||
13 | }
|
||||
Details: main.Animal has `name()`
|
||||
Details: main.Animal has `fn name(x main.Animal)`
|
||||
main.Cat has `fn name(c main.Cat, s string)`
|
||||
|
|
|
@ -4,4 +4,5 @@ vlib/v/checker/tests/unimplemented_interface_d.vv:12:6: error: `Cat` incorrectly
|
|||
12 | foo(Cat{})
|
||||
| ~~~~~
|
||||
13 | }
|
||||
Details: main.Animal has `speak(s string)`
|
||||
Details: main.Animal has `fn speak(x main.Animal, s string)`
|
||||
main.Cat has `fn speak(c main.Cat)`
|
||||
|
|
|
@ -5,11 +5,13 @@ vlib/v/checker/tests/unimplemented_interface_e.vv:12:6: error: `Cat` incorrectly
|
|||
| ~~~~~
|
||||
13 | _ = Animal(Cat{})
|
||||
14 | }
|
||||
Details: main.Animal has `speak(s string)`
|
||||
Details: main.Animal has `fn speak(x main.Animal, s string)`
|
||||
main.Cat has `fn speak(c main.Cat, s &string)`
|
||||
vlib/v/checker/tests/unimplemented_interface_e.vv:13:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`: expected `string`, not `&string` for parameter 1
|
||||
11 | fn main() {
|
||||
12 | foo(Cat{})
|
||||
13 | _ = Animal(Cat{})
|
||||
| ~~~~~~~~~~~~~
|
||||
14 | }
|
||||
Details: main.Animal has `speak(s string)`
|
||||
Details: main.Animal has `fn speak(x main.Animal, s string)`
|
||||
main.Cat has `fn speak(c main.Cat, s &string)`
|
||||
|
|
|
@ -4,4 +4,5 @@ vlib/v/checker/tests/unimplemented_interface_f.vv:11:13: error: `Cat` incorrectl
|
|||
11 | animals << Cat{}
|
||||
| ~~~~~
|
||||
12 | }
|
||||
Details: main.Animal has `speak(s string)`
|
||||
Details: main.Animal has `fn speak(x main.Animal, s string)`
|
||||
main.Cat has `fn speak(c main.Cat)`
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
vlib/v/parser/tests/interface_mutability_receiver.vv:23:18: error: `Doggo` incorrectly implements method `set_name` of interface `Animal`: expected `Animal` which is immutable, not `mut &Doggo`
|
||||
21 | dog := Doggo{'Doggo'}
|
||||
22 | println(dog.name)
|
||||
23 | set_animal_name(dog, 'Pupper')
|
||||
| ~~~
|
||||
24 | println(dog.name)
|
||||
25 | }
|
||||
Details: main.Animal has `fn set_name(x main.Animal, name string)`
|
||||
main.Doggo has `fn set_name(mut d main.Doggo, name string)`
|
|
@ -0,0 +1,25 @@
|
|||
// fixes https://github.com/vlang/v/issues/1081 and https://github.com/vlang/v/issues/7338, code by https://github.com/nedpals
|
||||
// copied from https://github.com/vlang/v/issues/7338
|
||||
struct Doggo {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn (mut d Doggo) set_name(name string) {
|
||||
d.name = name
|
||||
}
|
||||
|
||||
interface Animal {
|
||||
set_name(name string)
|
||||
}
|
||||
|
||||
fn set_animal_name(a Animal, name string) {
|
||||
a.set_name(name)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
dog := Doggo{'Doggo'}
|
||||
println(dog.name)
|
||||
set_animal_name(dog, 'Pupper')
|
||||
println(dog.name)
|
||||
}
|
|
@ -66,6 +66,7 @@ fn test_extract_basic() {
|
|||
|
||||
//////
|
||||
interface Iterator<T> {
|
||||
mut:
|
||||
next() ?T
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
interface Iter<T> {
|
||||
mut:
|
||||
next() ?T
|
||||
}
|
||||
|
||||
|
@ -24,7 +25,7 @@ fn iter<T>(arr []T) Iter<T> {
|
|||
}
|
||||
|
||||
fn test_generics_fn_return_generic_interface() {
|
||||
x := iter([1, 2, 3])
|
||||
mut x := iter([1, 2, 3])
|
||||
println(x)
|
||||
y := x.next() or { 0 }
|
||||
println(y)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
interface Iter<T> {
|
||||
mut:
|
||||
next() ?T
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
interface Iter<T> {
|
||||
mut:
|
||||
next() ?T
|
||||
}
|
||||
|
||||
|
@ -39,7 +40,7 @@ fn (mut it SkipIter<T>) next<T>() ?T {
|
|||
}
|
||||
|
||||
fn test_generics_interface_with_multi_generic_structs() {
|
||||
x := Iter<int>(ArrIter<int>{
|
||||
mut x := Iter<int>(ArrIter<int>{
|
||||
data: [1, 2, 3]
|
||||
})
|
||||
println(x)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
interface Iter<T, U> {
|
||||
mut:
|
||||
next() ?(T, U)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,13 @@ interface WalkerTalker {
|
|||
|
||||
interface Talker {
|
||||
nspeeches int
|
||||
mut:
|
||||
talk(msg string)
|
||||
}
|
||||
|
||||
interface Walker {
|
||||
nsteps int
|
||||
mut:
|
||||
walk(newx int, newy int)
|
||||
}
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ interface Speaker2 {
|
|||
name() string
|
||||
speak()
|
||||
return_speaker() Speaker2
|
||||
mut:
|
||||
return_speaker2() ?Speaker2
|
||||
}
|
||||
|
||||
|
@ -250,6 +251,7 @@ interface Animal {
|
|||
name() string
|
||||
name_detailed(pet_name string) string
|
||||
speak(s string)
|
||||
mut:
|
||||
set_breed(s string)
|
||||
}
|
||||
|
||||
|
@ -327,6 +329,31 @@ mut:
|
|||
my_field int
|
||||
}
|
||||
|
||||
// the opposite example of interface_mutability_receiver.vv
|
||||
// related to https://github.com/vlang/v/issues/1081 and https://github.com/vlang/v/issues/7338
|
||||
// test example code by https://github.com/nedpals copied and adapted from https://github.com/vlang/v/issues/7338
|
||||
// we accept immutable get_name even if the interface specified `mut` as it is consistent with function param behavior
|
||||
struct Dog2 {
|
||||
pub mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn (d Dog2) get_name() string {
|
||||
return d.name
|
||||
}
|
||||
|
||||
// mut get_name might be a bad example
|
||||
// we try to show that an interface can be more liberal in mut than the implementor,
|
||||
// while our error check is for the opposite case
|
||||
interface Animal2 {
|
||||
mut:
|
||||
get_name() string
|
||||
}
|
||||
|
||||
fn get_animal_name(mut a Animal2) string {
|
||||
return a.get_name()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut aa := AA{}
|
||||
mut ii := II(aa)
|
||||
|
@ -335,4 +362,7 @@ fn main() {
|
|||
assert ii.my_field == 123
|
||||
ii.my_field = 1234
|
||||
assert aa.my_field == 1234
|
||||
mut dog := Dog2{'Doggo'}
|
||||
println(dog.name)
|
||||
println(get_animal_name(mut dog))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue