Improved type safety for memory writes
This commit is contained in:
@ -8,6 +8,7 @@ import (
|
||||
"git.urbach.dev/cli/q/src/errors"
|
||||
"git.urbach.dev/cli/q/src/eval"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// CompileAssign compiles an assign statement.
|
||||
@ -56,11 +57,23 @@ func (f *Function) CompileAssign(node *ast.Assign) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !types.Is(rightValue.Type(), leftValue.Typ) {
|
||||
return errors.New(&errors.TypeMismatch{Encountered: rightValue.Type().Name(), Expected: leftValue.Typ.Name()}, f.File, right.Token.Position)
|
||||
}
|
||||
|
||||
leftSize := leftValue.Memory.Length
|
||||
rightSize := uint8(rightValue.Type().Size())
|
||||
|
||||
if rightSize != 0 && leftSize != rightSize {
|
||||
panic("memory store length mismatch")
|
||||
switch {
|
||||
case leftSize == 0:
|
||||
leftSize = rightSize
|
||||
leftValue.Memory.Length = rightSize
|
||||
case rightSize == 0:
|
||||
rightSize = leftSize
|
||||
}
|
||||
|
||||
if leftSize == 0 {
|
||||
return errors.New(errors.UnknownMemorySize, f.File, operation.Position)
|
||||
}
|
||||
|
||||
f.ValueToMemory(rightValue, leftValue.Memory)
|
||||
|
@ -21,28 +21,27 @@ func (f *Function) EvaluateArray(expr *expression.Expression) (*eval.Memory, err
|
||||
|
||||
defer f.UseVariable(base)
|
||||
|
||||
memory := asm.Memory{
|
||||
value := &eval.Memory{
|
||||
Memory: asm.Memory{
|
||||
Base: base.Value.Register,
|
||||
Offset: 0,
|
||||
OffsetRegister: -1,
|
||||
Length: byte(1),
|
||||
Length: 0,
|
||||
},
|
||||
}
|
||||
|
||||
if len(expr.Children) == 1 {
|
||||
pointer, isPointer := base.Value.Typ.(*types.Pointer)
|
||||
|
||||
if !isPointer {
|
||||
switch baseType := base.Value.Typ.(type) {
|
||||
case *types.Array:
|
||||
value.Typ = baseType.Of
|
||||
value.Memory.Length = byte(baseType.Of.Size())
|
||||
case *types.Pointer:
|
||||
value.Typ = baseType.To
|
||||
value.Memory.Length = byte(baseType.To.Size())
|
||||
default:
|
||||
return nil, errors.New(&errors.TypeMismatch{Encountered: base.Value.Typ.Name(), Expected: types.AnyPointer.Name()}, f.File, expr.Token.Position)
|
||||
}
|
||||
|
||||
// TODO: This is a hack that needs to be removed
|
||||
memory.Length = 8
|
||||
|
||||
value := &eval.Memory{
|
||||
Typ: pointer.To,
|
||||
Memory: memory,
|
||||
}
|
||||
|
||||
if len(expr.Children) == 1 {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
@ -59,34 +58,12 @@ func (f *Function) EvaluateArray(expr *expression.Expression) (*eval.Memory, err
|
||||
|
||||
switch index := index.(type) {
|
||||
case *eval.Number:
|
||||
memory.Offset = int8(index.Number)
|
||||
value.Memory.Offset = int8(index.Number)
|
||||
case *eval.Register:
|
||||
memory.OffsetRegister = index.Register
|
||||
value.Memory.OffsetRegister = index.Register
|
||||
default:
|
||||
panic(fmt.Errorf("%s: not implemented: %v", f.UniqueName, index))
|
||||
}
|
||||
|
||||
array, isArray := base.Value.Typ.(*types.Array)
|
||||
|
||||
if isArray {
|
||||
value := &eval.Memory{
|
||||
Typ: array.Of,
|
||||
Memory: memory,
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
pointer, isPointer := base.Value.Typ.(*types.Pointer)
|
||||
|
||||
if isPointer {
|
||||
value := &eval.Memory{
|
||||
Typ: pointer.To,
|
||||
Memory: memory,
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
panic("invalid type")
|
||||
}
|
||||
|
@ -21,5 +21,6 @@ var (
|
||||
MissingParameter = &Base{"Missing parameter"}
|
||||
MissingType = &Base{"Missing type"}
|
||||
NotImplemented = &Base{"Not implemented"}
|
||||
UnknownMemorySize = &Base{"Unknown memory size"}
|
||||
UntypedExpression = &Base{"Untyped expression"}
|
||||
)
|
||||
|
@ -26,8 +26,13 @@ func (s *Scanner) scanStruct(file *fs.File, tokens token.List, i int) (int, erro
|
||||
fieldPosition := i
|
||||
fieldName := tokens[i].Text(file.Bytes)
|
||||
i++
|
||||
fieldTypeName := tokens[i].Text(file.Bytes)
|
||||
typePosition := i
|
||||
|
||||
for i < len(tokens) && tokens[i].Kind != token.NewLine && tokens[i].Kind != token.BlockEnd {
|
||||
i++
|
||||
}
|
||||
|
||||
fieldTypeName := tokens[typePosition:i].Text(file.Bytes)
|
||||
|
||||
structure.AddField(&types.Field{
|
||||
Name: fieldName,
|
||||
|
@ -2,6 +2,6 @@ main() {
|
||||
writeToMemory(42)
|
||||
}
|
||||
|
||||
writeToMemory(p *any) {
|
||||
p[0] = 'A'
|
||||
writeToMemory(p *byte) {
|
||||
[p] = 'A'
|
||||
}
|
6
tests/errors/TypeMismatch6.q
Normal file
6
tests/errors/TypeMismatch6.q
Normal file
@ -0,0 +1,6 @@
|
||||
import mem
|
||||
|
||||
main() {
|
||||
a := mem.alloc(1)
|
||||
a[0] = int64(1)
|
||||
}
|
@ -49,11 +49,12 @@ var errs = []struct {
|
||||
{"MissingParameter3.q", errors.MissingParameter},
|
||||
{"MissingType.q", errors.MissingType},
|
||||
{"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}},
|
||||
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*any", Encountered: "int", ParameterName: "p"}},
|
||||
{"TypeMismatch.q", &errors.TypeMismatch{Expected: "*uint8", Encountered: "int", ParameterName: "p"}},
|
||||
{"TypeMismatch2.q", &errors.TypeMismatch{Expected: "[]any", Encountered: "int", ParameterName: "array"}},
|
||||
{"TypeMismatch3.q", &errors.TypeMismatch{Expected: "int", Encountered: "[]uint8"}},
|
||||
{"TypeMismatch4.q", &errors.TypeMismatch{Expected: "int", Encountered: "[]uint8"}},
|
||||
{"TypeMismatch5.q", &errors.TypeMismatch{Expected: "*any", Encountered: "int64"}},
|
||||
{"TypeMismatch6.q", &errors.TypeMismatch{Expected: "uint8", Encountered: "int64"}},
|
||||
{"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
{"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
{"UnknownIdentifier3.q", &errors.UnknownIdentifier{Name: "x"}},
|
||||
|
Reference in New Issue
Block a user