From 08ad1ddd48cbfe4dde52c65b5d535b1f74fb0359 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sun, 7 Sep 2025 16:45:45 +0200 Subject: [PATCH] Disallowed nil pointers --- docs/readme.md | 1 + lib/exec/fork_linux.q | 2 +- lib/exec/run_unix.q | 4 ++-- lib/exec/run_windows.q | 2 +- lib/io/io_windows.q | 4 ++-- src/core/TypeFromTokens.go | 2 ++ src/core/decompose.go | 15 ++++++++------- src/types/Common.go | 1 + src/types/Is.go | 17 ++++++----------- src/types/Union.go | 2 +- src/types/types_test.go | 8 ++++++-- 11 files changed, 31 insertions(+), 27 deletions(-) diff --git a/docs/readme.md b/docs/readme.md index 4498ad7..5778975 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -59,6 +59,7 @@ q build examples/hello --os [linux|mac|windows] --arch [x86|arm] ## News +- **2025-09-07**: Pointers cannot be nil. - **2025-09-03**: Error handling. - **2025-08-31**: Constant folding. - **2025-08-25**: Resource safety. diff --git a/lib/exec/fork_linux.q b/lib/exec/fork_linux.q index b35b815..23e728b 100644 --- a/lib/exec/fork_linux.q +++ b/lib/exec/fork_linux.q @@ -2,7 +2,7 @@ fork() -> int { return clone(sigchld, 0, 0, 0, 0) } -clone(flags uint, stack *any, parent *int, child *int, tls uint) -> int { +clone(flags uint, stack *any|nil, parent *int|nil, child *int|nil, tls uint) -> int { return syscall(_clone, flags, stack, parent, child, tls) } diff --git a/lib/exec/run_unix.q b/lib/exec/run_unix.q index f927fd8..b9a4a7a 100644 --- a/lib/exec/run_unix.q +++ b/lib/exec/run_unix.q @@ -27,10 +27,10 @@ run(path string) -> error { return exitCode } -execve(path *byte, argv *any, envp *any) -> error { +execve(path *byte, argv *any, envp *any|nil) -> error { return syscall(_execve, path, argv, envp) } -wait4(pid int, status *int32, options int, rusage *any) -> error { +wait4(pid int, status *int32, options int, rusage *any|nil) -> error { return syscall(_wait4, pid, status, options, rusage) } \ No newline at end of file diff --git a/lib/exec/run_windows.q b/lib/exec/run_windows.q index 3e04d81..c09d6a2 100644 --- a/lib/exec/run_windows.q +++ b/lib/exec/run_windows.q @@ -26,7 +26,7 @@ run(path string) -> error { extern { kernel32 { CloseHandle(handle *any) -> bool - CreateProcessA(applicationName *byte, commandLine *byte, processAttributes *any, threadAttributes *any, inheritHandles bool, creationFlags uint32, environment *uint16, currentDirectory *uint16, startupInfo *any, processInformation *any) -> (success bool) + CreateProcessA(applicationName *byte|nil, commandLine *byte, processAttributes *any|nil, threadAttributes *any|nil, inheritHandles bool, creationFlags uint32, environment *uint16|nil, currentDirectory *uint16|nil, startupInfo *any, processInformation *any) -> (success bool) WaitForSingleObject(handle *any, milliseconds uint32) -> uint32 } } \ No newline at end of file diff --git a/lib/io/io_windows.q b/lib/io/io_windows.q index e1bb8c3..c1203f0 100644 --- a/lib/io/io_windows.q +++ b/lib/io/io_windows.q @@ -35,7 +35,7 @@ writeTo(fd int, buffer string) -> (written int) { extern { kernel32 { GetStdHandle(device int64) -> (handle int64) - ReadFile(fd int64, buffer *byte, length uint32, read *uint32, overlapped *any) -> (success bool) - WriteFile(fd int64, buffer *byte, length uint32, written *uint32, overlapped *any) -> (success bool) + ReadFile(fd int64, buffer *byte, length uint32, read *uint32, overlapped *any|nil) -> (success bool) + WriteFile(fd int64, buffer *byte, length uint32, written *uint32, overlapped *any|nil) -> (success bool) } } \ No newline at end of file diff --git a/src/core/TypeFromTokens.go b/src/core/TypeFromTokens.go index f853fdc..4ef2365 100644 --- a/src/core/TypeFromTokens.go +++ b/src/core/TypeFromTokens.go @@ -112,6 +112,8 @@ func TypeFromTokens(tokens token.List, file *fs.File, env *Environment) (types.T return types.Float32, nil case "error": return types.Error, nil + case "nil": + return types.Nil, nil case "any": return types.Any, nil } diff --git a/src/core/decompose.go b/src/core/decompose.go index bce82e1..e98029b 100644 --- a/src/core/decompose.go +++ b/src/core/decompose.go @@ -43,13 +43,14 @@ func (f *Function) decompose(nodes []*expression.Expression, typeCheck []*ssa.Pa }, f.File, node.Source().StartPos) } - union, isUnion := expectedType.(*types.Union) - - if isUnion { - index := union.Index(valueType) - tag := f.Append(&ssa.Int{Int: index}) - args = append(args, tag) - } + // NOTE: The following code is disabled because it only applies to tagged unions. + // union, isUnion := expectedType.(*types.Union) + + // if isUnion { + // index := union.Index(valueType) + // tag := f.Append(&ssa.Int{Int: index}) + // args = append(args, tag) + // } } structure, isStruct := value.(*ssa.Struct) diff --git a/src/types/Common.go b/src/types/Common.go index ab14938..d9831da 100644 --- a/src/types/Common.go +++ b/src/types/Common.go @@ -12,6 +12,7 @@ var ( Int8 = &Base{name: "int8", size: 1} Float64 = &Base{name: "float64", size: 8} Float32 = &Base{name: "float32", size: 4} + Nil = &Base{name: "nil", size: 8} UInt64 = &Base{name: "uint64", size: 8} UInt32 = &Base{name: "uint32", size: 4} UInt16 = &Base{name: "uint16", size: 2} diff --git a/src/types/Is.go b/src/types/Is.go index 6d32409..0818815 100644 --- a/src/types/Is.go +++ b/src/types/Is.go @@ -6,22 +6,17 @@ func Is(a Type, b Type) bool { return true } - bUnion, bIsUnion := b.(*Union) - - if bIsUnion { - return bUnion.Index(a) != -1 - } - aPointer, aIsPointer := a.(*Pointer) bPointer, bIsPointer := b.(*Pointer) - if aIsPointer && bIsPointer && (bPointer.To == Any || aPointer.To == bPointer.To) { + if aIsPointer && bIsPointer && (aPointer.To == bPointer.To || bPointer.To == Any) { return true } - // TODO: Remove this temporary hack to allow integers as pointers - if bIsPointer && a == AnyInt { - return true + bUnion, bIsUnion := b.(*Union) + + if bIsUnion { + return bUnion.Index(a) != -1 } aResource, aIsResource := a.(*Resource) @@ -38,7 +33,7 @@ func Is(a Type, b Type) bool { if a == AnyInt || a == Error { switch b { - case Int64, Int32, Int16, Int8, UInt64, UInt32, UInt16, UInt8, Error, AnyInt: + case Int64, Int32, Int16, Int8, UInt64, UInt32, UInt16, UInt8, Error, Nil, AnyInt: return true default: return false diff --git a/src/types/Union.go b/src/types/Union.go index a09d350..abed94f 100644 --- a/src/types/Union.go +++ b/src/types/Union.go @@ -41,7 +41,7 @@ func (u *Union) Size() int { size = max(size, typ.Size()) } - return size + 8 + return size } // String returns the type name. diff --git a/src/types/types_test.go b/src/types/types_test.go index ae40559..56c06f9 100644 --- a/src/types/types_test.go +++ b/src/types/types_test.go @@ -38,8 +38,12 @@ func TestBasics(t *testing.T) { assert.False(t, types.Is(types.Any, types.Int)) assert.False(t, types.Is(types.Any, types.Float)) - // TODO: This check is currently disabled due to some temporary hacks, add it back later. - //assert.False(t, types.Is(types.AnyInt, types.AnyPointer)) +} + +func TestNil(t *testing.T) { + assert.False(t, types.Is(types.Nil, types.AnyPointer)) + assert.False(t, types.Is(types.AnyInt, types.AnyPointer)) + assert.True(t, types.Is(types.AnyInt, types.Nil)) } func TestUnions(t *testing.T) { -- 2.51.2