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.
|
||||
// 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.
|
||||
func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||
func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error) {
|
||||
var (
|
||||
pkg = f.Package
|
||||
pkgNode *expression.Expression
|
||||
@ -29,25 +29,12 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||
|
||||
switch name {
|
||||
case "len":
|
||||
return &Function{
|
||||
Output: []*Output{
|
||||
{
|
||||
Type: types.Int,
|
||||
},
|
||||
},
|
||||
}, f.CompileLen(root)
|
||||
return _len.OutputTypes, f.CompileLen(root)
|
||||
case "syscall":
|
||||
return nil, f.CompileSyscall(root)
|
||||
case "new":
|
||||
typ, err := f.CompileNew(root)
|
||||
|
||||
return &Function{
|
||||
Output: []*Output{
|
||||
{
|
||||
Type: typ,
|
||||
},
|
||||
},
|
||||
}, err
|
||||
return []types.Type{typ}, err
|
||||
case "delete":
|
||||
return nil, f.CompileDelete(root)
|
||||
case "store":
|
||||
@ -112,29 +99,16 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
encountered := ""
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
called, err := f.CompileCall(right)
|
||||
types, err := f.CompileCall(right)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -53,8 +53,8 @@ func (f *Function) CompileDefinition(node *ast.Define) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if called != nil {
|
||||
variable.Type = called.Output[count].Type
|
||||
if count < len(types) {
|
||||
variable.Type = types[count]
|
||||
}
|
||||
|
||||
f.RegisterRegister(asm.MOVE, variable.Register, f.CPU.Output[count])
|
||||
|
@ -4,17 +4,25 @@ import (
|
||||
"math"
|
||||
|
||||
"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/types"
|
||||
)
|
||||
|
||||
var _len = Function{OutputTypes: []types.Type{types.Int}}
|
||||
|
||||
// CompileLen returns the length of a slice.
|
||||
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 {
|
||||
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.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 errors.New(&errors.TypeMismatch{
|
||||
Encountered: typ.Name(),
|
||||
Expected: f.Output[i].Type.Name(),
|
||||
ParameterName: "",
|
||||
IsReturn: true,
|
||||
}, f.File, node.Values[i].Token.Position)
|
||||
return errors.New(&errors.TypeMismatch{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) {
|
||||
fn, err := f.CompileCall(node)
|
||||
types, err := f.CompileCall(node)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if register != f.CPU.Output[0] {
|
||||
f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0])
|
||||
}
|
||||
|
||||
if fn == nil || len(fn.Output) == 0 {
|
||||
return nil, err
|
||||
if len(types) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return fn.Output[0].Type, err
|
||||
return types[0], err
|
||||
}
|
||||
|
||||
if node.Token.Kind == token.Array {
|
||||
|
@ -11,19 +11,20 @@ import (
|
||||
// Function represents the smallest unit of code.
|
||||
type Function struct {
|
||||
register.Machine
|
||||
Package string
|
||||
Name string
|
||||
UniqueName string
|
||||
File *fs.File
|
||||
Body token.List
|
||||
Input []*Input
|
||||
Output []*Output
|
||||
Functions map[string]*Function
|
||||
Structs map[string]*types.Struct
|
||||
DLLs dll.List
|
||||
Err error
|
||||
deferred []func()
|
||||
count counter
|
||||
Package string
|
||||
Name string
|
||||
UniqueName string
|
||||
File *fs.File
|
||||
Body token.List
|
||||
Input []*Input
|
||||
Output []*Output
|
||||
OutputTypes []types.Type
|
||||
Functions map[string]*Function
|
||||
Structs map[string]*types.Struct
|
||||
DLLs dll.List
|
||||
Err error
|
||||
deferred []func()
|
||||
count counter
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return errors.New(&errors.UnknownType{Name: typeName}, f.File, param.tokens[1].Position)
|
||||
}
|
||||
|
||||
f.OutputTypes = append(f.OutputTypes, param.Type)
|
||||
}
|
||||
|
||||
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},
|
||||
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
||||
{"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"}},
|
||||
{"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}},
|
||||
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
|
Loading…
x
Reference in New Issue
Block a user