Improved type system checks
This commit is contained in:
parent
b2a9dc3aa7
commit
371059d08a
@ -14,7 +14,7 @@ import (
|
|||||||
// All call registers must hold the correct parameter values before the function invocation.
|
// All call registers must hold the correct parameter values before the function invocation.
|
||||||
// Registers that are in use must be saved if they are modified by the function.
|
// Registers that are in use must be saved if they are modified by the function.
|
||||||
// After the function call, they must be restored in reverse order.
|
// After the function call, they must be restored in reverse order.
|
||||||
func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error) {
|
||||||
var (
|
var (
|
||||||
pkg = f.Package
|
pkg = f.Package
|
||||||
pkgNode *expression.Expression
|
pkgNode *expression.Expression
|
||||||
@ -29,25 +29,12 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case "len":
|
case "len":
|
||||||
return &Function{
|
return _len.OutputTypes, f.CompileLen(root)
|
||||||
Output: []*Output{
|
|
||||||
{
|
|
||||||
Type: types.Int,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, f.CompileLen(root)
|
|
||||||
case "syscall":
|
case "syscall":
|
||||||
return nil, f.CompileSyscall(root)
|
return nil, f.CompileSyscall(root)
|
||||||
case "new":
|
case "new":
|
||||||
typ, err := f.CompileNew(root)
|
typ, err := f.CompileNew(root)
|
||||||
|
return []types.Type{typ}, err
|
||||||
return &Function{
|
|
||||||
Output: []*Output{
|
|
||||||
{
|
|
||||||
Type: typ,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, err
|
|
||||||
case "delete":
|
case "delete":
|
||||||
return nil, f.CompileDelete(root)
|
return nil, f.CompileDelete(root)
|
||||||
case "store":
|
case "store":
|
||||||
@ -112,29 +99,16 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !types.Is(typ, fn.Input[i].Type) {
|
if !types.Is(typ, fn.Input[i].Type) {
|
||||||
if parameters[i].Token.Kind == token.Number && parameters[i].Token.Text(f.File.Bytes) == "0" {
|
_, expectsPointer := fn.Input[i].Type.(*types.Pointer)
|
||||||
|
|
||||||
|
if expectsPointer && parameters[i].Token.Kind == token.Number && parameters[i].Token.Text(f.File.Bytes) == "0" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
encountered := ""
|
return nil, errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: fn.Input[i].Type.Name(), ParameterName: fn.Input[i].Name}, f.File, parameters[i].Token.Position)
|
||||||
expected := ""
|
|
||||||
|
|
||||||
if typ != nil {
|
|
||||||
encountered = typ.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
if fn.Input[i].Type != nil {
|
|
||||||
expected = fn.Input[i].Type.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New(&errors.TypeMismatch{
|
|
||||||
Encountered: encountered,
|
|
||||||
Expected: expected,
|
|
||||||
ParameterName: fn.Input[i].Name,
|
|
||||||
}, f.File, parameters[i].Token.Position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.CallSafe(fn, registers)
|
f.CallSafe(fn, registers)
|
||||||
return fn, nil
|
return fn.OutputTypes, nil
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
called, err := f.CompileCall(right)
|
types, err := f.CompileCall(right)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -53,8 +53,8 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if called != nil {
|
if count < len(types) {
|
||||||
variable.Type = called.Output[count].Type
|
variable.Type = types[count]
|
||||||
}
|
}
|
||||||
|
|
||||||
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
|
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
|
||||||
|
@ -4,17 +4,25 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/asm"
|
"git.akyoto.dev/cli/q/src/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
"git.akyoto.dev/cli/q/src/expression"
|
"git.akyoto.dev/cli/q/src/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _len = Function{OutputTypes: []types.Type{types.Int}}
|
||||||
|
|
||||||
// CompileLen returns the length of a slice.
|
// CompileLen returns the length of a slice.
|
||||||
func (f *Function) CompileLen(root *expression.Expression) error {
|
func (f *Function) CompileLen(root *expression.Expression) error {
|
||||||
_, register, isTemporary, err := f.Evaluate(root.Children[1])
|
typ, register, isTemporary, err := f.Evaluate(root.Children[1])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !types.Is(typ, types.AnyArray) {
|
||||||
|
return errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: types.AnyArray.Name(), ParameterName: "array"}, f.File, root.Children[1].Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
f.SaveRegister(f.CPU.Output[0])
|
f.SaveRegister(f.CPU.Output[0])
|
||||||
f.MemoryRegister(asm.LOAD, asm.Memory{Base: register, Offset: -8, OffsetRegister: math.MaxUint8, Length: 8}, f.CPU.Output[0])
|
f.MemoryRegister(asm.LOAD, asm.Memory{Base: register, Offset: -8, OffsetRegister: math.MaxUint8, Length: 8}, f.CPU.Output[0])
|
||||||
|
|
||||||
|
@ -30,12 +30,7 @@ func (f *Function) CompileReturn(node *ast.Return) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New(&errors.TypeMismatch{
|
return errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: f.Output[i].Type.Name(), ParameterName: "", IsReturn: true}, f.File, node.Values[i].Token.Position)
|
||||||
Encountered: typ.Name(),
|
|
||||||
Expected: f.Output[i].Type.Name(),
|
|
||||||
ParameterName: "",
|
|
||||||
IsReturn: true,
|
|
||||||
}, f.File, node.Values[i].Token.Position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,17 +24,21 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ast.IsFunctionCall(node) {
|
if ast.IsFunctionCall(node) {
|
||||||
fn, err := f.CompileCall(node)
|
types, err := f.CompileCall(node)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if register != f.CPU.Output[0] {
|
if register != f.CPU.Output[0] {
|
||||||
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
|
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if fn == nil || len(fn.Output) == 0 {
|
if len(types) == 0 {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn.Output[0].Type, err
|
return types[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.Token.Kind == token.Array {
|
if node.Token.Kind == token.Array {
|
||||||
|
@ -11,19 +11,20 @@ import (
|
|||||||
// Function represents the smallest unit of code.
|
// Function represents the smallest unit of code.
|
||||||
type Function struct {
|
type Function struct {
|
||||||
register.Machine
|
register.Machine
|
||||||
Package string
|
Package string
|
||||||
Name string
|
Name string
|
||||||
UniqueName string
|
UniqueName string
|
||||||
File *fs.File
|
File *fs.File
|
||||||
Body token.List
|
Body token.List
|
||||||
Input []*Input
|
Input []*Input
|
||||||
Output []*Output
|
Output []*Output
|
||||||
Functions map[string]*Function
|
OutputTypes []types.Type
|
||||||
Structs map[string]*types.Struct
|
Functions map[string]*Function
|
||||||
DLLs dll.List
|
Structs map[string]*types.Struct
|
||||||
Err error
|
DLLs dll.List
|
||||||
deferred []func()
|
Err error
|
||||||
count counter
|
deferred []func()
|
||||||
|
count counter
|
||||||
}
|
}
|
||||||
|
|
||||||
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
// counter stores how often a certain statement appeared so we can generate a unique label from it.
|
||||||
|
@ -40,6 +40,8 @@ func (f *Function) ResolveTypes() error {
|
|||||||
if param.Type == nil {
|
if param.Type == nil {
|
||||||
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
|
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.OutputTypes = append(f.OutputTypes, param.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
3
tests/errors/TypeMismatch2.q
Normal file
3
tests/errors/TypeMismatch2.q
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
main() {
|
||||||
|
len(123)
|
||||||
|
}
|
@ -47,6 +47,7 @@ var errs = []struct {
|
|||||||
{"MissingType.q", errors.MissingType},
|
{"MissingType.q", errors.MissingType},
|
||||||
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
||||||
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*Any", Encountered: "Int64", ParameterName: "p"}},
|
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*Any", Encountered: "Int64", ParameterName: "p"}},
|
||||||
|
{"TypeMismatch2.q", &errors.TypeMismatch{Expected: "[]Any", Encountered: "Int64", ParameterName: "array"}},
|
||||||
{"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}},
|
{"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}},
|
||||||
{"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}},
|
{"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}},
|
||||||
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user