From b3389bcbf6762499d1b9c2ac16a363a2f1f42967 Mon Sep 17 00:00:00 2001 From: chibash Date: Sun, 2 Aug 2026 21:48:31 +0900 Subject: [PATCH 1/6] renames Vector class to FixedArray class --- .../transpiler/code-generator/c-runtime.ts | 8 +++---- .../code-generator/code-generator.ts | 4 ++-- lang/src/transpiler/type-checker.ts | 8 +++---- lang/src/transpiler/types.ts | 4 ++-- .../code-generator/code-generator2.test.ts | 8 +++---- lang/tests/transpiler/type-checker.test.ts | 2 +- microcontroller/core/src/c-runtime.c | 22 +++++++++---------- .../reference/language/built-in-objects.md | 10 ++++----- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lang/src/transpiler/code-generator/c-runtime.ts b/lang/src/transpiler/code-generator/c-runtime.ts index 6697251d..3b9f5aaf 100644 --- a/lang/src/transpiler/code-generator/c-runtime.ts +++ b/lang/src/transpiler/code-generator/c-runtime.ts @@ -6,7 +6,7 @@ import { Integer, Float, BooleanT, StringT, Void, Null, Any, ObjectType, objectType, FunctionType, StaticType, isPrimitiveType, typeToString, ArrayType, sameType, encodeType, isSubtype, ByteArrayClass, UnionType, EnumType, - VectorClass} from '../types' + FixedArrayClass} from '../types' import { InstanceType, ClassTable, StaticPropertyInfo } from '../classes' import { VariableEnv } from './variables' @@ -380,7 +380,7 @@ export function makeBoxedValue(type: StaticType, value?: string) { export function arrayElementGetter(t: StaticType | undefined, arrayType: StaticType | undefined, node: AST.Node) { if (arrayType instanceof InstanceType && arrayType.name() === ByteArrayClass) return '(*gc_bytearray_get(' - else if (arrayType instanceof InstanceType && arrayType.name() === VectorClass) + else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) return '(gc_vector_get(' else if (arrayType === Any) return '(gc_safe_array_get(' @@ -399,7 +399,7 @@ export function arrayElementSetter(arrayType: StaticType | undefined) { throw new Error('unknown array type') else if (arrayType === Any) return 'gc_safe_array_set(' - else if (arrayType instanceof InstanceType && arrayType.name() === VectorClass) + else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) return 'gc_vector_set(' else return `gc_array_set(` @@ -595,7 +595,7 @@ export function makeInstance(clazz: InstanceType, func: () => string) { const name = clazz.name() if (name === ByteArrayClass) return 'gc_new_bytearray(false' - else if (name === VectorClass) + else if (name === FixedArrayClass) return 'gc_new_vector(' else return `${constructorNameInC(name)}(${func()}gc_new_object(&${classObjectNameInC(name)})` diff --git a/lang/src/transpiler/code-generator/code-generator.ts b/lang/src/transpiler/code-generator/code-generator.ts index 79a77d5a..1c397396 100644 --- a/lang/src/transpiler/code-generator/code-generator.ts +++ b/lang/src/transpiler/code-generator/code-generator.ts @@ -4,7 +4,7 @@ import * as AST from '@babel/types' import { runBabelParser, ErrorLog, CodeWriter } from '../utils' import { Integer, BooleanT, Void, Any, ObjectType, FunctionType, StaticType, ByteArrayClass, isPrimitiveType, encodeType, sameType, typeToString, ArrayType, objectType, - StringT, UnionType, VectorClass, StringType, + StringT, UnionType, FixedArrayClass, StringType, EnumType} from '../types' import * as visitor from '../visitor' import { getCoercionFlag, getStaticType } from '../names' @@ -1693,7 +1693,7 @@ export class CodeGenerator extends visitor.NodeVisitor { this.visit(node.object, env) this.result.write(`, ${cr.getArrayLengthIndex(BooleanT)})`) } - else if (propertyName === ArrayType.lengthProperty && objType.name() === VectorClass) { + else if (propertyName === ArrayType.lengthProperty && objType.name() === FixedArrayClass) { this.result.write(cr.getObjectPrimitiveProperty(Integer)) this.visit(node.object, env) this.result.write(`, ${cr.getArrayLengthIndex(Any)})`) diff --git a/lang/src/transpiler/type-checker.ts b/lang/src/transpiler/type-checker.ts index af0d83bb..3d565a59 100644 --- a/lang/src/transpiler/type-checker.ts +++ b/lang/src/transpiler/type-checker.ts @@ -4,7 +4,7 @@ import * as AST from '@babel/types' import { ErrorLog } from './utils' import * as visitor from './visitor' -import { ArrayType, StaticType, ByteArrayClass, isPrimitiveType, UnionType, VectorClass, StringType } from './types' +import { ArrayType, StaticType, ByteArrayClass, isPrimitiveType, UnionType, FixedArrayClass, StringType } from './types' import { Integer, Float, BooleanT, StringT, Void, Null, Any, @@ -82,7 +82,7 @@ export default class TypeChecker extends visitor.NodeVisi addBuiltinTypes(node: AST.Node, names: NameTable) { this.addBuiltinClass(names, node, ByteArrayClass, [Integer, Integer]) - this.addBuiltinClass(names, node, VectorClass, [Integer, Any]) + this.addBuiltinClass(names, node, FixedArrayClass, [Integer, Any]) } // if constructorParams is undefined, this class may not be instantiated. @@ -1465,7 +1465,7 @@ export default class TypeChecker extends visitor.NodeVisi this.addStaticType(node, Integer) this.result = Integer } - else if (this.result instanceof InstanceType && this.result.name() === VectorClass) { + else if (this.result instanceof InstanceType && this.result.name() === FixedArrayClass) { this.addStaticType(node, Any) this.result = Any } @@ -1497,7 +1497,7 @@ export default class TypeChecker extends visitor.NodeVisi const unboxed = type.unboxedProperties() return unboxed === undefined || unboxed <= typeAndIndex[1] } - else if (propertyName === ArrayType.lengthProperty && (type.name() === ByteArrayClass || type.name() === VectorClass)) { + else if (propertyName === ArrayType.lengthProperty && (type.name() === ByteArrayClass || type.name() === FixedArrayClass)) { this.assert(readonly, 'cannot change .length', node.property) this.result = Integer return false // an uboxed value. diff --git a/lang/src/transpiler/types.ts b/lang/src/transpiler/types.ts index 929f4150..f5ed5b52 100644 --- a/lang/src/transpiler/types.ts +++ b/lang/src/transpiler/types.ts @@ -9,7 +9,7 @@ export const Null = 'null' export const Any = 'any' export const ByteArrayClass = 'Uint8Array' // Uint8Array is also used as byte[]. -export const VectorClass = 'Vector' +export const FixedArrayClass = 'FixedArray' export type StaticType = 'integer' | 'float' | 'boolean' | 'string' | 'void' | 'null' | 'any' | ObjectType | FunctionType | UnionType | EnumType @@ -127,7 +127,7 @@ export class FunctionType extends CompositeType { } // This class represents both an array of instances and an array of primitive types. -// It does not represent built-in array-like types such as Uint8Array and Vector. +// It does not represent built-in array-like types such as Uint8Array and FixedArray. export class ArrayType extends ObjectType { // see builtinPropertiesAndMethods in classes.ts static readonly lengthProperty = 'length' diff --git a/lang/tests/transpiler/code-generator/code-generator2.test.ts b/lang/tests/transpiler/code-generator/code-generator2.test.ts index 47cbf738..aa8f6144 100644 --- a/lang/tests/transpiler/code-generator/code-generator2.test.ts +++ b/lang/tests/transpiler/code-generator/code-generator2.test.ts @@ -1358,14 +1358,14 @@ print(Foo.baz) expect(importAndCompileAndRun(src, imp.importer(), imp.init(), imp.files(), imp.path)).toBe('3\nfoo\ntrue\n') }) -test('Vector class', () => { +test('FixeArray class', () => { const src = ` function bar() { const a = new Uint8Array(3, 7) print(a.length) } function foo() { - const a = new Vector(3, null) + const a = new FixedArray(3, null) print(a.length) a[0] = 13 a[1] = 'foo' @@ -1374,7 +1374,7 @@ test('Vector class', () => { print(b.length) b[1] = 73 print(b[1]) - print((b as Vector)[2]) + print((b as FixedArray)[2]) const c: any = b print(c[0]) } @@ -1681,7 +1681,7 @@ test('integer[]#push, pop, etc', () => { print(ary.shift())` expect(() => compileAndRun(src3, destFile)).toThrow(/unknown property name: shift/) - const src4 = `const ary = new Vector(3, 0) + const src4 = `const ary = new FixedArray(3, 0) print(ary.shift())` expect(() => compileAndRun(src4, destFile)).toThrow(/unknown property name: shift/) diff --git a/lang/tests/transpiler/type-checker.test.ts b/lang/tests/transpiler/type-checker.test.ts index a24c154e..8d7f1704 100644 --- a/lang/tests/transpiler/type-checker.test.ts +++ b/lang/tests/transpiler/type-checker.test.ts @@ -249,7 +249,7 @@ test('InstanceType.subclasses() and ClassTable.roots()', () => { const roots = table?.classTable()?.roots() expect(roots !== undefined && roots.length).toBe(4) expect(roots !== undefined && roots[0].name()).toBe('Uint8Array') - expect(roots !== undefined && roots[1].name()).toBe('Vector') + expect(roots !== undefined && roots[1].name()).toBe('FixedArray') expect(roots !== undefined && roots[2].name()).toBe('Foo') expect(roots !== undefined && roots[3].name()).toBe('Foo2') }) diff --git a/microcontroller/core/src/c-runtime.c b/microcontroller/core/src/c-runtime.c index dc55c969..de7303e1 100644 --- a/microcontroller/core/src/c-runtime.c +++ b/microcontroller/core/src/c-runtime.c @@ -518,7 +518,7 @@ void* gc_method_lookup(value_t obj, uint32_t index) { return get_objects_class(value_to_ptr(obj))->vtbl[index]; } -// IS_ARRAY_TYPE(t) is true when t is an array object, Uint8Array, or Vector. +// IS_ARRAY_TYPE(t) is true when t is an array object, Uint8Array, or FixeArray. // t is an object accessible by the [] operator. #define IS_ARRAY_TYPE(clazz) (clazz != NULL && (clazz)->array_type_name != NULL) @@ -1337,12 +1337,12 @@ bool gc_is_boolarray(value_t v) { // A fixed-length array -CLASS_OBJECT(class_Vector, 1) = { - .clazz = { .size = -1, .start_index = 1, .name = "Vector", - .superclass = &object_class.clazz, .array_type_name = "'Vector'", .table = DEFAULT_PTABLE, .mtable = DEFAULT_MTABLE }}; +CLASS_OBJECT(class_FixedArray, 1) = { + .clazz = { .size = -1, .start_index = 1, .name = "FixedArray", + .superclass = &object_class.clazz, .array_type_name = "'FixedArray'", .table = DEFAULT_PTABLE, .mtable = DEFAULT_MTABLE }}; value_t safe_value_to_vector(bool nullable, value_t v) { - return safe_value_to_value(nullable, &class_Vector.clazz, v); + return safe_value_to_value(nullable, &class_FixedArray.clazz, v); } /* @@ -1358,7 +1358,7 @@ value_t gc_new_vector(int32_t n, value_t init_value) { n = 0; pointer_t obj = allocate_heap(n + 1); - set_object_header(obj, &class_Vector.clazz); + set_object_header(obj, &class_FixedArray.clazz); obj->body[0] = n; for (int i = 0; i < n; i++) obj->body[i + 1] = init_value; @@ -1388,7 +1388,7 @@ static value_t duplicate_vector(int32_t n, int32_t offset, value_t vec) { n = len + offset; pointer_t obj = allocate_heap(n + 1); - set_object_header(obj, &class_Vector.clazz); + set_object_header(obj, &class_FixedArray.clazz); obj->body[0] = n; for (int i = 1; i <= offset; i++) obj->body[i] = VALUE_UNDEF; @@ -1414,7 +1414,7 @@ value_t gc_vector_get(value_t obj, int32_t idx) { if (0 <= idx && idx < len) return objp->body[idx + 1]; else { - runtime_index_error(idx, len, "Vector.get"); + runtime_index_error(idx, len, "FixedArray.get"); return VALUE_UNDEF; } } @@ -1428,7 +1428,7 @@ value_t gc_vector_set(value_t obj, int32_t index, value_t new_value) { return new_value; } else { - runtime_index_error(index, len, "Vector.set"); + runtime_index_error(index, len, "FixedArray.set"); return 0; } } @@ -1680,7 +1680,7 @@ value_t gc_safe_array_get(value_t obj, int32_t idx) { return int_to_value(*gc_bytearray_get(obj, idx)); else if (clazz == &boolarray_object.clazz) return bool_to_value(*gc_bytearray_get(obj, idx)); - else if (clazz == &class_Vector.clazz) + else if (clazz == &class_FixedArray.clazz) return gc_vector_get(obj, idx); else if (IS_ARRAY_TYPE(clazz)) // for arrays of value_t return *gc_array_get(obj, idx); @@ -1702,7 +1702,7 @@ value_t gc_safe_array_set(value_t obj, int32_t idx, value_t new_value) { uint8_t v = *gc_bytearray_get(obj, idx) = safe_value_to_bool(new_value); return bool_to_value(v); } - else if (clazz == &class_Vector.clazz) + else if (clazz == &class_FixedArray.clazz) return gc_vector_set(obj, idx, new_value); else if (IS_ARRAY_TYPE(clazz)) // for arrays of value_t return gc_array_set(obj, idx, new_value); diff --git a/website/docs/reference/language/built-in-objects.md b/website/docs/reference/language/built-in-objects.md index 524500d1..e7ed6a22 100644 --- a/website/docs/reference/language/built-in-objects.md +++ b/website/docs/reference/language/built-in-objects.md @@ -184,19 +184,19 @@ print(arr.length) // 3 The second argumemnt to the constructor of `Uint8Array` cannot be omitted. -### `Vector` class +### `FixedArray` class -A `Vector` object is a fixed-length array. Its element type is `any`. +A `FixedArray` object is a fixed-length array. Its element type is `any`. ```tsx -let arr = new Vector(3, undefined) // create an array containing 3 elements. Their initial value is undefined. +let arr = new FixedArray(3, undefined) // create an array containing 3 elements. Their initial value is undefined. print(arr[1]) // undefined arr[0] = 7 print(arr[0]) // 7 print(arr.length) // 3 ``` -A `Vector` object is accessible by the `[]` operator as an array of type `integer[]` is. +A `FixedArray` object is accessible by the `[]` operator as an array of type `integer[]` is. ### Properties and methods @@ -222,7 +222,7 @@ The following methods are available: Here, `T` is an element type. However, arrays of `integer`, `float`, or `boolean` do not accept these methods. -`Uint8Array`, or `Vector` do not accept them, either. +`Uint8Array`, or `FixedArray` do not accept them, either. They are fixed-length arrays. Currently, methods such as `map`, `filter`, etc., are not supported. From ab7ff9cb415cdc4934caaf7de8b446b6b21b62f9 Mon Sep 17 00:00:00 2001 From: chibash Date: Mon, 3 Aug 2026 01:33:16 +0900 Subject: [PATCH 2/6] changes the consistency rule. any[] ~ t[] where T is a concrete type such as integer. --- .../transpiler/code-generator/c-runtime.ts | 10 +++++-- .../code-generator/code-generator.ts | 9 ++++-- lang/src/transpiler/types.ts | 12 ++++---- .../code-generator/code-generator.test.ts | 8 +++--- .../code-generator/code-generator2.test.ts | 28 +++++++++++++++++++ microcontroller/core/include/c-runtime.h | 1 + microcontroller/core/src/c-runtime.c | 19 ++++++++++++- .../reference/language/built-in-objects.md | 24 ++++++++++++++-- 8 files changed, 93 insertions(+), 18 deletions(-) diff --git a/lang/src/transpiler/code-generator/c-runtime.ts b/lang/src/transpiler/code-generator/c-runtime.ts index 3b9f5aaf..77fd2a5b 100644 --- a/lang/src/transpiler/code-generator/c-runtime.ts +++ b/lang/src/transpiler/code-generator/c-runtime.ts @@ -391,7 +391,7 @@ export function arrayElementGetter(t: StaticType | undefined, arrayType: StaticT else if (t === BooleanT) return '(*gc_bytearray_get(' else - return `(*gc_array_get(` + return `(gc_safe_array_get(` } export function arrayElementSetter(arrayType: StaticType | undefined) { @@ -402,7 +402,7 @@ export function arrayElementSetter(arrayType: StaticType | undefined) { else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) return 'gc_vector_set(' else - return `gc_array_set(` + return `gc_safe_array_set(` } export const accumulateInUnknownArray = 'gc_safe_array_acc' @@ -601,10 +601,14 @@ export function makeInstance(clazz: InstanceType, func: () => string) { return `${constructorNameInC(name)}(${func()}gc_new_object(&${classObjectNameInC(name)})` } -export function methodLookup(method: [StaticType, number, InstanceType?], func: string) { +export function methodLookup(method: [StaticType, number, (InstanceType | ArrayType)?], func: string) { return `((${funcTypeToCType(method[0])})gc_method_lookup(${func}, ${method[1]}))` } +export function isArrayObject(f: string) { + return `safe_anyarray_to_anyarrayobj(${f})` +} + export const dynamicMethodCall = 'gc_dynamic_method_call' export function isInstanceOf(t: InstanceType) { diff --git a/lang/src/transpiler/code-generator/code-generator.ts b/lang/src/transpiler/code-generator/code-generator.ts index 1c397396..c62ab3bb 100644 --- a/lang/src/transpiler/code-generator/code-generator.ts +++ b/lang/src/transpiler/code-generator/code-generator.ts @@ -1486,7 +1486,12 @@ export class CodeGenerator extends visitor.NodeVisitor { } else { // a method call on a typed object - this.result.write(`, ${cr.methodLookup(method[0], func)}(${func}`) + let funcExpr = func + const minfo = method[0] + if (minfo[2] instanceof ArrayType && minfo[2].elementType === Any) + funcExpr = cr.isArrayObject(func) + + this.result.write(`, ${cr.methodLookup(method[0], funcExpr)}(${func}`) } } else { @@ -1740,7 +1745,7 @@ export class CodeGenerator extends visitor.NodeVisitor { // This returns method_info, method_name, or undefined. visitIfMethodExpr(node: AST.MemberExpression, env: VariableEnv): - [ method: [method_type: StaticType, method_table_index: number, declaring_class?: InstanceType], + [ method: [method_type: StaticType, method_table_index: number, declaring_class?: InstanceType | ArrayType], is_call_on_super_or_not: boolean] | string | undefined { if (node.computed) return undefined diff --git a/lang/src/transpiler/types.ts b/lang/src/transpiler/types.ts index f5ed5b52..a4a59db0 100644 --- a/lang/src/transpiler/types.ts +++ b/lang/src/transpiler/types.ts @@ -162,17 +162,17 @@ export class ArrayType extends ObjectType { return false } - findMethod(name: string): [StaticType, number] | undefined { + findMethod(name: string): [StaticType, number, this] | undefined { if (isPrimitiveType(this.elementType)) return undefined // primitive-type arrays do not have methods else if (name === ArrayType.pushMethod) - return [new FunctionType(Integer, [this.elementType]), 0] + return [new FunctionType(Integer, [this.elementType]), 0, this] else if (name === ArrayType.popMethod) - return [new FunctionType(new UnionType([this.elementType, Null]), []), 1] + return [new FunctionType(new UnionType([this.elementType, Null]), []), 1, this] else if (name === ArrayType.unshiftMethod) - return [new FunctionType(Integer, [this.elementType]), 2] + return [new FunctionType(Integer, [this.elementType]), 2, this] else if (name === ArrayType.shiftMethod) - return [new FunctionType(new UnionType([this.elementType, Null]), []), 3] + return [new FunctionType(new UnionType([this.elementType, Null]), []), 3, this] else return undefined } @@ -366,6 +366,8 @@ export function isConsistent(t1: StaticType, t2: StaticType) { return t1 !== t2 && t2 !== Void else if (t2 === Any) return t1 !== Void + else if (t1 instanceof ArrayType && t2 instanceof ArrayType) + return isConsistent(t1.elementType, t2.elementType) else return false } diff --git a/lang/tests/transpiler/code-generator/code-generator.test.ts b/lang/tests/transpiler/code-generator/code-generator.test.ts index 7891bc2e..59cadb15 100644 --- a/lang/tests/transpiler/code-generator/code-generator.test.ts +++ b/lang/tests/transpiler/code-generator/code-generator.test.ts @@ -1139,12 +1139,12 @@ test('any-type array', () => { const src2 = ` function foo(n: integer): integer { const arr = [ n ] - const s: any[] = (arr as any) // int[] as any (error) + const s: any[] = (arr as any) // int[] as any (not an error) return n } print(foo(4)) ` - expect(() => compileAndRun(src2)).toThrow(/runtime type error/) + expect(compileAndRun(src2)).toBe('4\n') }) test('any-type array 2', () => { @@ -1166,12 +1166,12 @@ test('any-type array 2', () => { const src2 = ` function foo(n: string): string { const arr = [ n ] - const s: any[] = (arr as any) // string[] as any (error) + const s: any[] = (arr as any) // string[] as any (not an error) return arr[0] } print(foo('test'))` - expect(() => compileAndRun(src2)).toThrow(/runtime type error: any\[\]/) + expect(compileAndRun(src2)).toBe('test\n') }) test('any-type array element', () => { diff --git a/lang/tests/transpiler/code-generator/code-generator2.test.ts b/lang/tests/transpiler/code-generator/code-generator2.test.ts index aa8f6144..99fc023c 100644 --- a/lang/tests/transpiler/code-generator/code-generator2.test.ts +++ b/lang/tests/transpiler/code-generator/code-generator2.test.ts @@ -2095,3 +2095,31 @@ test('wrong array construction', () => { ` expect(() => { compileAndRun(src, destFile) }).toThrow(/string.*line 2.*\n.*integer.*line 3.*\n.*boolean.*line 4.*\n/) }) + +test('consistency between any[] and specific arrays', () => { + const src = ` + function foo() { + const a: any[] = [1, 2, 3] + const b: integer[] = a + print(a[0] + a[1] + a[2]) + print(b[0] + b[1] + b[2]) + print(b.length) + } + foo() + ` + + expect(compileAndRun(src, destFile)).toBe('6\n6\n3\n') +}) + +test('consistency between any[] and specific arrays and method calls', () => { + const src = ` + function foo() { + const a: integer[] = [1, 2, 3] + const b: any[] = a + print(b.pop()) + } + foo() + ` + + expect(() => compileAndRun(src, destFile)).toThrow(/runtime type error.*anyarray/) +}) diff --git a/microcontroller/core/include/c-runtime.h b/microcontroller/core/include/c-runtime.h index b2c94a10..0ca2a9cc 100644 --- a/microcontroller/core/include/c-runtime.h +++ b/microcontroller/core/include/c-runtime.h @@ -269,6 +269,7 @@ extern value_t CR_SECTION gc_make_vector(int32_t n, ...); extern bool CR_SECTION gc_is_instance_of_array(value_t obj); extern value_t CR_SECTION safe_value_to_anyarray(bool nullable, value_t v); +extern value_t CR_SECTION safe_anyarray_to_anyarrayobj(value_t v); extern value_t CR_SECTION gc_new_array(const class_object* clazz, int32_t n, value_t init_value); extern value_t CR_SECTION gc_copy_array(const class_object* clazz, value_t array); extern value_t CR_SECTION gc_make_array(const class_object* clazz, int32_t n, ...); diff --git a/microcontroller/core/src/c-runtime.c b/microcontroller/core/src/c-runtime.c index de7303e1..18fe7ab5 100644 --- a/microcontroller/core/src/c-runtime.c +++ b/microcontroller/core/src/c-runtime.c @@ -274,6 +274,8 @@ value_t safe_value_to_value(bool nullable, const class_object* const clazz, valu return v; } +// also see safe_value_to_anyarray() + #define ANY_OP_FUNC(name, op, rest) \ value_t any_##name(value_t a, value_t b) {\ if (is_int_value(a)) {\ @@ -1463,7 +1465,22 @@ static CLASS_OBJECT(anyarray_object, 4) = { .vtbl = { gc_array_push, gc_array_pop, gc_array_unshift, gc_array_shift } }}; value_t safe_value_to_anyarray(bool nullable, value_t v) { - return safe_value_to_value(nullable, &anyarray_object.clazz, v); + if (nullable && v == VALUE_NULL) + return v; + + if (!IS_ARRAY_TYPE(gc_get_class_of(v))) + runtime_type_error("safe_value_to_anyarray"); + + return v; +} + +// Makes sure that v is an instance of anyarray_object. +// It raises a runtime type error if not, for example, if v is an integer array. +value_t safe_anyarray_to_anyarrayobj(value_t v) { + if (!gc_is_anyarray(v)) + runtime_type_error("safe_anyarray_to_anyarrayobj"); + + return v; } static inline int32_t real_array_length(int32_t n) { return ((n + 1) & ~7) + 7; } diff --git a/website/docs/reference/language/built-in-objects.md b/website/docs/reference/language/built-in-objects.md index e7ed6a22..1bb21531 100644 --- a/website/docs/reference/language/built-in-objects.md +++ b/website/docs/reference/language/built-in-objects.md @@ -58,9 +58,8 @@ BlueScript currently supports arrays of `integer`, `float`, `boolean`, `string`, array types, and `any`-type. Their names are `T[]`, where `T` is an element type. -Array types are invariant. For example, `integer[]` is not a subtype of `any[]` or its super type. -But array types can be implicitly converted into `any`-type, and vice versa. -In other words, a reference to an array of `integer`, `any`, etc. is implicitly converted into an `any`-type value. +Array types can be implicitly converted into `any`-type, and vice versa. +In other words, a reference to an array of `integer`, `float`, etc. is implicitly converted into an `any`-type value. An `any`-type value is also implicitly converted into a reference to an array if the `any`-type value points to an array object of that array type. Otherwise, a runtime error is thrown. @@ -71,6 +70,25 @@ let a: any = iarr let i: integer = a[0] // a[0] is an `any`-type value although iarr[0] is an integer ``` +An array type is invariant. For example, `A[]` is not a subtype of `B[]` or its super type even when the type `A` is a subtype of `B`. +However, an array type may be implicitly converted into `any[]`. + +```tsx +let iarr: integer[] = [1, 2, 3] +let a: any[] = iarr +let i: integer = a[0] // a[0] is an `any`-type value +a[0] = 'one' // runtime error. The array can contain only integers. +``` + +Arrays can be constructed to contain values of `any`-type. +Their types are `any[]`. + +```tsx +let arr = [1, "Two", 3.0] // arr's type is any[] +let a = arr[0] // a[0] is an `any`-type value +arr[0] = "One" // An any type of value can be stored. +``` + ### Array Literals Array literals are defined using square brackets, with elements separated by commas. From 6febb56790fe93e5652f5fc534c1f6a3eceb7982 Mon Sep 17 00:00:00 2001 From: chibash Date: Mon, 3 Aug 2026 01:57:20 +0900 Subject: [PATCH 3/6] renames vector to fixarray in the runtime written in C --- .../transpiler/code-generator/c-runtime.ts | 6 +- .../code-generator/code-generator.ts | 4 +- microcontroller/core/include/c-runtime.h | 12 +-- microcontroller/core/src/c-runtime.c | 70 +++++++-------- microcontroller/core/test/c-runtime-test.c | 90 +++++++++---------- microcontroller/core/test/c-runtime-test2.c | 28 +++--- 6 files changed, 105 insertions(+), 105 deletions(-) diff --git a/lang/src/transpiler/code-generator/c-runtime.ts b/lang/src/transpiler/code-generator/c-runtime.ts index 77fd2a5b..d7f58121 100644 --- a/lang/src/transpiler/code-generator/c-runtime.ts +++ b/lang/src/transpiler/code-generator/c-runtime.ts @@ -381,7 +381,7 @@ export function arrayElementGetter(t: StaticType | undefined, arrayType: StaticT if (arrayType instanceof InstanceType && arrayType.name() === ByteArrayClass) return '(*gc_bytearray_get(' else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) - return '(gc_vector_get(' + return '(gc_fixarray_get(' else if (arrayType === Any) return '(gc_safe_array_get(' else if (t === Integer || t instanceof EnumType) @@ -400,7 +400,7 @@ export function arrayElementSetter(arrayType: StaticType | undefined) { else if (arrayType === Any) return 'gc_safe_array_set(' else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) - return 'gc_vector_set(' + return 'gc_fixarray_set(' else return `gc_safe_array_set(` } @@ -596,7 +596,7 @@ export function makeInstance(clazz: InstanceType, func: () => string) { if (name === ByteArrayClass) return 'gc_new_bytearray(false' else if (name === FixedArrayClass) - return 'gc_new_vector(' + return 'gc_new_fixarray(' else return `${constructorNameInC(name)}(${func()}gc_new_object(&${classObjectNameInC(name)})` } diff --git a/lang/src/transpiler/code-generator/code-generator.ts b/lang/src/transpiler/code-generator/code-generator.ts index c62ab3bb..6ec2efb3 100644 --- a/lang/src/transpiler/code-generator/code-generator.ts +++ b/lang/src/transpiler/code-generator/code-generator.ts @@ -228,9 +228,9 @@ export class CodeGenerator extends visitor.NodeVisitor { }) if (frees.length > 0) { - // all the arguments to gc_make_vector must be reachable + // all the arguments to gc_make_fixarray must be reachable // from the garbage-collection root. - obj = `gc_make_vector(${frees.length}${args})` + obj = `gc_make_fixarray(${frees.length}${args})` } } diff --git a/microcontroller/core/include/c-runtime.h b/microcontroller/core/include/c-runtime.h index 0ca2a9cc..df6ec1ac 100644 --- a/microcontroller/core/include/c-runtime.h +++ b/microcontroller/core/include/c-runtime.h @@ -260,12 +260,12 @@ extern int32_t CR_SECTION gc_bytearray_length(value_t obj); extern uint8_t* CR_SECTION gc_bytearray_get(value_t obj, int32_t index); extern bool CR_SECTION gc_is_boolarray(value_t v); -extern value_t CR_SECTION safe_value_to_vector(bool nullable, value_t v); -extern value_t CR_SECTION gc_new_vector(int32_t n, value_t init_value); -extern int32_t CR_SECTION gc_vector_length(value_t obj); -extern value_t CR_SECTION gc_vector_get(value_t obj, int32_t index); -extern value_t CR_SECTION gc_vector_set(value_t obj, int32_t index, value_t new_value); -extern value_t CR_SECTION gc_make_vector(int32_t n, ...); +extern value_t CR_SECTION safe_value_to_fixarray(bool nullable, value_t v); +extern value_t CR_SECTION gc_new_fixarray(int32_t n, value_t init_value); +extern int32_t CR_SECTION gc_fixarray_length(value_t obj); +extern value_t CR_SECTION gc_fixarray_get(value_t obj, int32_t index); +extern value_t CR_SECTION gc_fixarray_set(value_t obj, int32_t index, value_t new_value); +extern value_t CR_SECTION gc_make_fixarray(int32_t n, ...); extern bool CR_SECTION gc_is_instance_of_array(value_t obj); extern value_t CR_SECTION safe_value_to_anyarray(bool nullable, value_t v); diff --git a/microcontroller/core/src/c-runtime.c b/microcontroller/core/src/c-runtime.c index 18fe7ab5..5ce3b3f3 100644 --- a/microcontroller/core/src/c-runtime.c +++ b/microcontroller/core/src/c-runtime.c @@ -15,9 +15,9 @@ root_set.values[0] = a; root_set.values[1] = b; - root_set.values[2] = obj = gc_new_vector(2); - gc_vector_set(obj, int_to_value(0), a); - gc_vector_set(obj, int_to_value(1), b); + root_set.values[2] = obj = gc_new_fixarray(2); + gc_fixarray_set(obj, int_to_value(0), a); + gc_fixarray_set(obj, int_to_value(1), b); DELETE_ROOT_SET(root_set); return obj; @@ -709,7 +709,7 @@ const void* gc_function_object_ptr(value_t obj, int index) { // returns an instance of boxed_value or boxed_raw_value, returned by gc_new_box() etc. value_t gc_function_captured_value(value_t obj, int index) { value_t vec = value_to_ptr(obj)->body[2]; - return gc_vector_get(vec, index); + return gc_fixarray_get(vec, index); } // boxed_value and boxed_raw_value are classes for boxing. Their instances hold one value_t value @@ -1343,17 +1343,17 @@ CLASS_OBJECT(class_FixedArray, 1) = { .clazz = { .size = -1, .start_index = 1, .name = "FixedArray", .superclass = &object_class.clazz, .array_type_name = "'FixedArray'", .table = DEFAULT_PTABLE, .mtable = DEFAULT_MTABLE }}; -value_t safe_value_to_vector(bool nullable, value_t v) { +value_t safe_value_to_fixarray(bool nullable, value_t v) { return safe_value_to_value(nullable, &class_FixedArray.clazz, v); } /* - A fixed-length array. We call it a vector. - n: the number of vector elements. + A fixed-length array. We call it a fixarray. + n: the number of array elements. 1st word is the number of elements. 2nd, 3rd, ... words hold elements. */ -value_t gc_new_vector(int32_t n, value_t init_value) { +value_t gc_new_fixarray(int32_t n, value_t init_value) { ROOT_SET(rootset, 1) rootset.values[0] = init_value; if (n < 0) @@ -1369,21 +1369,21 @@ value_t gc_new_vector(int32_t n, value_t init_value) { return ptr_to_value(obj); } -inline static value_t* fast_vector_get(value_t obj, int32_t index) { +inline static value_t* fast_fixarray_get(value_t obj, int32_t index) { pointer_t objp = value_to_ptr(obj); return &objp->body[index + 1]; } -inline static void fast_vector_set(value_t obj, uint32_t index, value_t new_value) { +inline static void fast_fixarray_set(value_t obj, uint32_t index, value_t new_value) { pointer_t objp = value_to_ptr(obj); gc_write_barrier(objp, new_value); objp->body[index + 1] = new_value; } -static value_t duplicate_vector(int32_t n, int32_t offset, value_t vec) { +static value_t duplicate_fixarray(int32_t n, int32_t offset, value_t vec) { ROOT_SET(rootset, 1) rootset.values[0] = vec; - int32_t len = gc_vector_length(vec); + int32_t len = gc_fixarray_length(vec); if (n < 0) n = 0; else if (n < len + offset) @@ -1405,12 +1405,12 @@ static value_t duplicate_vector(int32_t n, int32_t offset, value_t vec) { return ptr_to_value(obj); } -int32_t gc_vector_length(value_t obj) { +int32_t gc_fixarray_length(value_t obj) { pointer_t objp = value_to_ptr(obj); return objp->body[0]; } -value_t gc_vector_get(value_t obj, int32_t idx) { +value_t gc_fixarray_get(value_t obj, int32_t idx) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= idx && idx < len) @@ -1421,7 +1421,7 @@ value_t gc_vector_get(value_t obj, int32_t idx) { } } -value_t gc_vector_set(value_t obj, int32_t index, value_t new_value) { +value_t gc_fixarray_set(value_t obj, int32_t index, value_t new_value) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= index && index < len) { @@ -1435,16 +1435,16 @@ value_t gc_vector_set(value_t obj, int32_t index, value_t new_value) { } } -/* The given vector elements are not stored in a root set. +/* The given array elements are not stored in a root set. A caller function must guarantee that they are reachable from the root. */ -value_t gc_make_vector(int32_t n, ...) { +value_t gc_make_fixarray(int32_t n, ...) { va_list args; - value_t array = gc_new_vector(n, VALUE_UNDEF); + value_t array = gc_new_fixarray(n, VALUE_UNDEF); va_start(args, n); for (int32_t i = 0; i < n; i++) - fast_vector_set(array, i, va_arg(args, value_t)); + fast_fixarray_set(array, i, va_arg(args, value_t)); va_end(args); return array; } @@ -1494,14 +1494,14 @@ value_t gc_new_array(const class_object* clazz, int32_t n, value_t init_value) { pointer_t obj = gc_allocate_object(clazz == NULL ? &anyarray_object.clazz : clazz); rootset.values[1] = ptr_to_value(obj); const int32_t size = real_array_length(n); - value_t vec = gc_new_vector(size, init_value); - pointer_t vecp = value_to_ptr(vec); + value_t arr = gc_new_fixarray(size, init_value); + pointer_t arrp = value_to_ptr(arr); if (init_value != VALUE_UNDEF) for (int i = n + 1; i <= size; i++) - vecp->body[i] = VALUE_UNDEF; + arrp->body[i] = VALUE_UNDEF; - obj->body[1] = vec; - // the length must be less than or equal to the length of the vector. + obj->body[1] = arr; + // the length must be less than or equal to the length of the . obj->body[0] = n; DELETE_ROOT_SET(rootset) return ptr_to_value(obj); @@ -1554,7 +1554,7 @@ static value_t gc_grow_array(value_t obj, int32_t addedElements, int32_t offset) int32_t size = vecp->body[0]; if (new_n > size) { int32_t new_size = real_array_length(new_n); - value_t new_vec = duplicate_vector(new_size, offset, vec); + value_t new_vec = duplicate_fixarray(new_size, offset, vec); objp->body[1] = new_vec; } else @@ -1585,7 +1585,7 @@ value_t gc_make_array(const class_object* clazz, int32_t n, ...) { va_start(args, n); for (int32_t i = 0; i < n; i++) - fast_vector_set(arrayp->body[1], i, va_arg(args, value_t)); + fast_fixarray_set(arrayp->body[1], i, va_arg(args, value_t)); va_end(args); return array; @@ -1600,7 +1600,7 @@ value_t* gc_array_get(value_t obj, int32_t idx) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= idx && idx < len) - return fast_vector_get(objp->body[1], idx); + return fast_fixarray_get(objp->body[1], idx); else { runtime_index_error(idx, len, "Array.get"); return 0; @@ -1611,7 +1611,7 @@ value_t gc_array_set(value_t obj, int32_t index, value_t new_value) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= index && index < len) { - fast_vector_set(objp->body[1], index, new_value); + fast_fixarray_set(objp->body[1], index, new_value); return new_value; } else { runtime_index_error(index, len, "Array.set"); @@ -1626,7 +1626,7 @@ int32_t gc_array_push(value_t obj, value_t new_value) { gc_grow_array(obj, 1, 0); pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; - fast_vector_set(objp->body[1], len - 1, new_value); + fast_fixarray_set(objp->body[1], len - 1, new_value); DELETE_ROOT_SET(rootset) return len; } @@ -1637,8 +1637,8 @@ value_t gc_array_pop(value_t obj) { if (len == 0) return VALUE_UNDEF; - value_t value = gc_vector_get(objp->body[1], len - 1); - gc_vector_set(objp->body[1], len - 1, VALUE_UNDEF); + value_t value = gc_fixarray_get(objp->body[1], len - 1); + gc_fixarray_set(objp->body[1], len - 1, VALUE_UNDEF); objp->body[0] = len - 1; return value; } @@ -1649,7 +1649,7 @@ int32_t gc_array_unshift(value_t obj, value_t new_value) { rootset.values[1] = new_value; gc_grow_array(obj, 1, 1); pointer_t objp = value_to_ptr(obj); - fast_vector_set(objp->body[1], 0, new_value); + fast_fixarray_set(objp->body[1], 0, new_value); DELETE_ROOT_SET(rootset) return objp->body[0]; } @@ -1660,7 +1660,7 @@ value_t gc_array_shift(value_t obj) { if (len == 0) return VALUE_UNDEF; - value_t value = gc_vector_get(objp->body[1], 0); + value_t value = gc_fixarray_get(objp->body[1], 0); gc_grow_array(obj, -1, -1); return value; } @@ -1698,7 +1698,7 @@ value_t gc_safe_array_get(value_t obj, int32_t idx) { else if (clazz == &boolarray_object.clazz) return bool_to_value(*gc_bytearray_get(obj, idx)); else if (clazz == &class_FixedArray.clazz) - return gc_vector_get(obj, idx); + return gc_fixarray_get(obj, idx); else if (IS_ARRAY_TYPE(clazz)) // for arrays of value_t return *gc_array_get(obj, idx); else { @@ -1720,7 +1720,7 @@ value_t gc_safe_array_set(value_t obj, int32_t idx, value_t new_value) { return bool_to_value(v); } else if (clazz == &class_FixedArray.clazz) - return gc_vector_set(obj, idx, new_value); + return gc_fixarray_set(obj, idx, new_value); else if (IS_ARRAY_TYPE(clazz)) // for arrays of value_t return gc_array_set(obj, idx, new_value); else { diff --git a/microcontroller/core/test/c-runtime-test.c b/microcontroller/core/test/c-runtime-test.c index ff9e16db..d60f93bc 100644 --- a/microcontroller/core/test/c-runtime-test.c +++ b/microcontroller/core/test/c-runtime-test.c @@ -111,25 +111,25 @@ static value_t gc_new_array2(int32_t n) { return gc_new_array(NULL, n, VALUE_UNDEF); } -static value_t gc_new_vector2(int32_t n) { - return gc_new_vector(n, VALUE_UNDEF); +static value_t gc_new_fixarray2(int32_t n) { + return gc_new_fixarray(n, VALUE_UNDEF); } static value_t gc_new_array3(int32_t n) { ROOT_SET(root_set, 1) - root_set.values[0] = gc_new_vector2(n); - value_t obj = gc_new_vector2(1); - gc_vector_set(obj, 0, root_set.values[0]); + root_set.values[0] = gc_new_fixarray2(n); + value_t obj = gc_new_fixarray2(1); + gc_fixarray_set(obj, 0, root_set.values[0]); DELETE_ROOT_SET(root_set) return obj; } static value_t gc_array3_get(value_t obj, int32_t index) { - return gc_vector_get(gc_vector_get(obj, 0), index); + return gc_fixarray_get(gc_fixarray_get(obj, 0), index); } static void gc_array3_set(value_t obj, int32_t index, value_t value) { - gc_vector_set(gc_vector_get(obj, 0), index, value); + gc_fixarray_set(gc_fixarray_get(obj, 0), index, value); } // Test functions @@ -233,7 +233,7 @@ void test_String() { check_string_length(s1); check_string_length(s); - check_string_length(gc_new_vector(3, VALUE_NULL)); + check_string_length(gc_new_fixarray(3, VALUE_NULL)); } static value_t gc_bytearray_set(value_t obj, value_t index, value_t new_value) { @@ -258,8 +258,8 @@ void test_bytearray() { Assert_equals(gc_bytearray_length(arr2), 7); } -static value_t gc_vector_setter(value_t obj, value_t index, value_t new_value) { - return gc_vector_set(obj, value_to_int(index), new_value); +static value_t gc_fixarray_setter(value_t obj, value_t index, value_t new_value) { + return gc_fixarray_set(obj, value_to_int(index), new_value); } void test_copy_intarray() { @@ -473,12 +473,12 @@ void test_copy_array() { Assert_equals(*gc_array_get(arr2b, 1), int_to_value(2)); Assert_equals(*gc_array_get(arr2b, 2), int_to_value(3)); - // Test 3: Copy from vector (fixed array) - value_t vec = gc_new_vector(3, VALUE_UNDEF); + // Test 3: Copy from fixarray (fixed length array) + value_t vec = gc_new_fixarray(3, VALUE_UNDEF); root_set.values[0] = vec; - gc_vector_set(vec, 0, int_to_value(5)); - gc_vector_set(vec, 1, int_to_value(15)); - gc_vector_set(vec, 2, int_to_value(25)); + gc_fixarray_set(vec, 0, int_to_value(5)); + gc_fixarray_set(vec, 1, int_to_value(15)); + gc_fixarray_set(vec, 2, int_to_value(25)); value_t arr3 = gc_copy_array(NULL, vec); root_set.values[1] = arr3; Assert_equals(gc_array_length(arr3), 3); @@ -570,15 +570,15 @@ void test_copy_arrays() { DELETE_ROOT_SET(root_set); } -void test_vector() { - value_t arr = gc_new_vector2(4); - value_t arr2 = gc_new_vector2(4); +void test_fixarray() { + value_t arr = gc_new_fixarray2(4); + value_t arr2 = gc_new_fixarray2(4); for (int i = 0; i < 4; i++) - Assert_equals(gc_vector_setter(arr2, int_to_value(i), int_to_value(i)), int_to_value(i)); + Assert_equals(gc_fixarray_setter(arr2, int_to_value(i), int_to_value(i)), int_to_value(i)); for (int i = 0; i < 4; i++) - gc_vector_setter(arr, i, int_to_value(i)); + gc_fixarray_setter(arr, i, int_to_value(i)); for (int i = 0; i < 4; i++) { - value_t e = gc_vector_get(arr2, int_to_value(i)); + value_t e = gc_fixarray_get(arr2, int_to_value(i)); Assert_equals(value_to_int(e), i); } Assert_equals(value_to_int(gc_array_length(arr)), 4); @@ -606,7 +606,7 @@ void test_allocate_heap() { value_t index = 2; value_t vec_size = heap_size / 1024; for (int i = 0; i < 1024; i++) { - value_t arr = gc_new_vector2(vec_size - 2); + value_t arr = gc_new_fixarray2(vec_size - 2); Assert_pequals(value_to_ptr(arr), &heap_memory[index]); index += vec_size; } @@ -625,7 +625,7 @@ void test_root_set() { root_set.values[0] = gc_new_string("hello"); value_t obj; for (int i = 0; i < 3; i++) - obj = gc_new_vector2(4); + obj = gc_new_fixarray2(4); gc_run(); Assert_equals(heap_memory[0], 4); @@ -645,7 +645,7 @@ void test_root_set2() { root_set.values[0] = gc_new_string("hello"); root_set.values[1] = gc_new_string("hello2"); for (int i = 0; i < 3; i++) - gc_new_vector2(3); + gc_new_fixarray2(3); root_set.values[0] = VALUE_NULL; gc_run(); @@ -660,13 +660,13 @@ void test_root_set2() { void test_nested_root_set2() { ROOT_SET(root_set, 3); - root_set.values[0] = gc_new_vector2(1); + root_set.values[0] = gc_new_fixarray2(1); DELETE_ROOT_SET(root_set); } void test_nested_root_set3() { ROOT_SET(root_set, 3); - root_set.values[0] = gc_new_vector2(1); + root_set.values[0] = gc_new_fixarray2(1); gc_run(); DELETE_ROOT_SET(root_set); } @@ -679,7 +679,7 @@ void test_nested_root_set() { root_set.values[0] = gc_new_string("hello"); root_set.values[1] = gc_new_string("hello2"); for (int i = 0; i < 3; i++) - gc_new_vector2(3); + gc_new_fixarray2(3); test_nested_root_set2(); root_set.values[0] = VALUE_NULL; @@ -690,7 +690,7 @@ void test_nested_root_set() { Assert_equals(heap_memory[3], 2); Assert_equals(heap_memory[6], 32); Assert_equals(heap_memory[7], 22); - Assert_pequals(get_objects_class((pointer_t)&heap_memory[28]), &class_Vector); + Assert_pequals(get_objects_class((pointer_t)&heap_memory[28]), &class_FixedArray); Assert_equals(heap_memory[32], heap_size); Assert_equals(heap_memory[33], heap_size - 32); @@ -776,14 +776,14 @@ void test_gc_liveness2() { ROOT_SET(root_set, 3); value_t obj, obj2, obj3, obj4; - root_set.values[0] = obj = gc_new_vector2(4); - obj4 = gc_new_vector2(1); - gc_vector_setter(obj, int_to_value(0), obj2 = gc_new_bytearray(true, 8, 0)); + root_set.values[0] = obj = gc_new_fixarray2(4); + obj4 = gc_new_fixarray2(1); + gc_fixarray_setter(obj, int_to_value(0), obj2 = gc_new_bytearray(true, 8, 0)); gc_bytearray_set_raw_word(obj2, 0, obj4); - gc_new_vector2(1); - gc_new_vector2(1); - gc_vector_setter(obj, int_to_value(1), obj3 = gc_new_vector2(2)); - gc_vector_setter(obj3, int_to_value(0), obj); + gc_new_fixarray2(1); + gc_new_fixarray2(1); + gc_fixarray_setter(obj, int_to_value(1), obj3 = gc_new_fixarray2(2)); + gc_fixarray_setter(obj3, int_to_value(0), obj); root_set.values[1] = gc_new_string("test"); gc_run(); @@ -808,11 +808,11 @@ void test_gc_sweep() { root_set.values[1] = gc_new_string("test3"); root_set.values[2] = gc_new_string("test4"); gc_new_string("test5"); - gc_new_vector2(2); - gc_new_vector2(3); - root_set.values[3] = obj = gc_new_vector2(4); - obj2 = gc_new_vector2(3); - gc_vector_setter(obj, 0, obj2); + gc_new_fixarray2(2); + gc_new_fixarray2(3); + root_set.values[3] = obj = gc_new_fixarray2(4); + obj2 = gc_new_fixarray2(3); + gc_fixarray_setter(obj, 0, obj2); gc_run(); @@ -869,11 +869,11 @@ void test_gc_write_barrier() { root_set.values[1] = gc_new_string("test3"); root_set.values[2] = gc_new_string("test4"); gc_new_string("test5"); - gc_new_vector2(2); - gc_new_vector2(3); - root_set.values[3] = obj = gc_new_vector2(4); - obj2 = gc_new_vector2(3); - gc_vector_setter(obj, 0, obj2); + gc_new_fixarray2(2); + gc_new_fixarray2(3); + root_set.values[3] = obj = gc_new_fixarray2(4); + obj2 = gc_new_fixarray2(3); + gc_fixarray_setter(obj, 0, obj2); gc_run(); Assert_true(is_live_object(obj1)); diff --git a/microcontroller/core/test/c-runtime-test2.c b/microcontroller/core/test/c-runtime-test2.c index dfb7468d..2fecf3d5 100644 --- a/microcontroller/core/test/c-runtime-test2.c +++ b/microcontroller/core/test/c-runtime-test2.c @@ -291,12 +291,12 @@ void test_array_push() { value_t arr = gc_new_array(&anyarray_object.clazz, i, int_to_value(i)); value_t arrvec = value_to_ptr(arr)->body[1]; Assert_equals(gc_array_length(arr), i); - Assert_equals(gc_vector_length(arrvec), real_len); + Assert_equals(gc_fixarray_length(arrvec), real_len); Assert_equals(value_to_ptr(arrvec)->body[0], real_len); for (int j = 0; j < i; j++) - Assert_equals(*fast_vector_get(arrvec, j), int_to_value(i)); + Assert_equals(*fast_fixarray_get(arrvec, j), int_to_value(i)); for (int j = i; j < real_len; ++j) - Assert_equals(*fast_vector_get(arrvec, j), VALUE_UNDEF); + Assert_equals(*fast_fixarray_get(arrvec, j), VALUE_UNDEF); if (i > 0) gc_array_set(arr, 0, int_to_value(70 + i)); Assert_equals(value_to_ptr(arr)->body[0], i); @@ -308,14 +308,14 @@ void test_array_push() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; if (i > 0) - Assert_equals(*fast_vector_get(vec, 0), int_to_value(70 + i)); + Assert_equals(*fast_fixarray_get(vec, 0), int_to_value(70 + i)); for (int k = 1; k < i; k++) - Assert_equals(*fast_vector_get(vec, k), int_to_value(i)); + Assert_equals(*fast_fixarray_get(vec, k), int_to_value(i)); Assert_true(len >= i + j + 1); - Assert_equals(*fast_vector_get(vec, i + j), int_to_value(90 + j)); + Assert_equals(*fast_fixarray_get(vec, i + j), int_to_value(90 + j)); for (int k = i + j + 1; k < len; k++) - Assert_equals(*fast_vector_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); } } @@ -334,10 +334,10 @@ void test_array_pop() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; for (int k = 0; k < i - j - 1; k++) - Assert_equals(*fast_vector_get(vec, k), int_to_value(100 + k)); + Assert_equals(*fast_fixarray_get(vec, k), int_to_value(100 + k)); Assert_true(len >= i - j - 1); for (int k = i - j - 1; k < len; k++) - Assert_equals(*fast_vector_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); } Assert_equals(gc_array_length(arr), 0); Assert_equals(gc_array_pop(arr), VALUE_UNDEF); @@ -357,14 +357,14 @@ void test_array_unshift() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; for (int k = 0; k < j + 1; k++) - Assert_equals(*fast_vector_get(vec, k), int_to_value(90 + j - k)); + Assert_equals(*fast_fixarray_get(vec, k), int_to_value(90 + j - k)); for (int k = j + 1; k < i + j + 1; k++) - Assert_equals(*fast_vector_get(vec, k), int_to_value(100 + k - j - 1)); + Assert_equals(*fast_fixarray_get(vec, k), int_to_value(100 + k - j - 1)); Assert_true(len >= i + j + 1); for (int k = i + j + 1; k < len; k++) - Assert_equals(*fast_vector_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); } } } @@ -381,10 +381,10 @@ void test_array_shift() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; for (int k = 0; k < i - j - 1; k++) - Assert_equals(*fast_vector_get(vec, k), int_to_value(100 + k + j + 1)); + Assert_equals(*fast_fixarray_get(vec, k), int_to_value(100 + k + j + 1)); Assert_true(len >= i - j - 1); for (int k = i - j - 1; k < len; k++) - Assert_equals(*fast_vector_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); } Assert_equals(gc_array_length(arr), 0); Assert_equals(gc_array_shift(arr), VALUE_UNDEF); From 3414e8a2187d7c3770fa109b0227c457038d0165 Mon Sep 17 00:00:00 2001 From: chibash Date: Fri, 14 Aug 2026 01:42:52 +0900 Subject: [PATCH 4/6] supports the int32 type --- lang/src/transpiler/classes.ts | 4 +- .../transpiler/code-generator/c-runtime.ts | 37 +++--- .../code-generator/code-generator.ts | 22 +++- lang/src/transpiler/type-checker.ts | 87 ++++++++------ lang/src/transpiler/types.ts | 45 +++++--- .../code-generator/code-generator.test.ts | 109 +++++++++++++++++- lang/tests/transpiler/type-checker.test.ts | 33 ++++++ .../reference/language/built-in-objects.md | 22 ++-- website/docs/reference/language/intro.md | 5 +- .../reference/language/primitive-types.md | 13 ++- 10 files changed, 292 insertions(+), 85 deletions(-) diff --git a/lang/src/transpiler/classes.ts b/lang/src/transpiler/classes.ts index 490011eb..f42e1b22 100644 --- a/lang/src/transpiler/classes.ts +++ b/lang/src/transpiler/classes.ts @@ -252,7 +252,7 @@ export class InstanceType extends ObjectType { const k = this.superClass.unboxedProperties() if (k === undefined || k !== size) { this.numOfUnboxed = k - return + return false // cannot move properties } index = size @@ -270,6 +270,8 @@ export class InstanceType extends ObjectType { if (!isPrimitiveType(value[0])) value[1] = index++ } + + return true } findConstructor() { return this.constructorFunction } diff --git a/lang/src/transpiler/code-generator/c-runtime.ts b/lang/src/transpiler/code-generator/c-runtime.ts index d7f58121..bc6f4feb 100644 --- a/lang/src/transpiler/code-generator/c-runtime.ts +++ b/lang/src/transpiler/code-generator/c-runtime.ts @@ -2,11 +2,12 @@ import * as AST from '@babel/types' import { ErrorLog } from '../utils' -import { Integer, Float, BooleanT, StringT, Void, Null, Any, +import { Integer, Int32, Float, BooleanT, StringT, Void, Null, Any, ObjectType, objectType, FunctionType, - StaticType, isPrimitiveType, typeToString, ArrayType, sameType, encodeType, isSubtype, + StaticType, isPrimitiveType, isIntegerLike, typeToString, ArrayType, sameType, encodeType, isSubtype, ByteArrayClass, UnionType, EnumType, - FixedArrayClass} from '../types' + FixedArrayClass, + isIntegerType} from '../types' import { InstanceType, ClassTable, StaticPropertyInfo } from '../classes' import { VariableEnv } from './variables' @@ -51,6 +52,7 @@ function typeToCType2(type: StaticType): string { else switch (type) { case Integer: + case Int32: return 'int32_t' case Float: return 'float' @@ -82,7 +84,7 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un return '(' if (sameType(from, to)) - if (from === Integer || from instanceof EnumType) + if (from === Integer || from === Int32 || from instanceof EnumType) return '(int32_t)(' else if (from === Float) return '(float)(' @@ -100,23 +102,24 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un switch (to) { case Integer: + case Int32: if (from === Float) return '(int32_t)(' - else if (from === BooleanT || from instanceof EnumType) + else if (isIntegerType(from) || from === BooleanT || from instanceof EnumType) return '(' else if (from === Any || from instanceof UnionType) return 'safe_value_to_int(' else break case Float: - if (from === Integer || from === BooleanT || from instanceof EnumType) + if (from === Integer || from === Int32 || from === BooleanT || from instanceof EnumType) return '(float)(' else if (from === Any || from instanceof UnionType) return 'safe_value_to_float(' else break case BooleanT: - if (from === Integer || from instanceof EnumType) + if (from === Integer || from === Int32 || from instanceof EnumType) return '(' else if (from === Float) return '(int32_t)(' @@ -133,6 +136,8 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un switch (from) { case Integer: return 'int_to_value(' + case Int32: + throw typeConversionError(from, to, node) case Float: return 'float_to_value(' case BooleanT: @@ -155,7 +160,7 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un if (to === objectType) return 'safe_value_to_object(false, ' else if (to instanceof ArrayType) { - if (to.elementType === Integer || to.elementType instanceof EnumType) + if (isIntegerLike(to.elementType)) return 'safe_value_to_intarray(false, ' else if (to.elementType === Float) return 'safe_value_to_floatarray(false, ' @@ -196,7 +201,7 @@ function typeConversionToUnion(from: StaticType, to: UnionType, env: VariableEnv if (objType === objectType) return 'safe_value_to_object(true, ' else if (objType instanceof ArrayType) { - if (objType.elementType === Integer || objType.elementType instanceof EnumType) + if (isIntegerLike(objType.elementType)) return 'safe_value_to_intarray(true, ' else if (objType.elementType === Float) return 'safe_value_to_floatarray(true, ' @@ -281,7 +286,7 @@ export const anyAddMember = 'any_add_member' export function power(type: StaticType | undefined) { if (type === Float) return '(float)double_power(' - else if (type === Integer) + else if (type === Integer || type === Int32) return '(int32_t)double_power(' else throw new Error('bad operand types for **') @@ -384,7 +389,7 @@ export function arrayElementGetter(t: StaticType | undefined, arrayType: StaticT return '(gc_fixarray_get(' else if (arrayType === Any) return '(gc_safe_array_get(' - else if (t === Integer || t instanceof EnumType) + else if (t === Integer || t === Int32 || t instanceof EnumType) return '(*gc_intarray_get(' else if (t === Float) return '(*gc_floatarray_get(' @@ -410,7 +415,7 @@ export const accumulateInUnknownArray = 'gc_safe_array_acc' // makes an array object from elements export function arrayFromElements(arrayType: ArrayType, env: VariableEnv) { const t = arrayType.elementType - if (t === Integer || t instanceof EnumType) + if (isIntegerLike(t)) return 'gc_make_intarray(' else if (t === Float) return 'gc_make_floatarray(' @@ -424,7 +429,7 @@ export function arrayFromElements(arrayType: ArrayType, env: VariableEnv) { export function arrayFromSize(arrayType: ArrayType, env: VariableEnv) { const t = arrayType.elementType - if (t === Integer || t instanceof EnumType) + if (isIntegerLike(t)) return 'gc_new_intarray(' else if (t === Float) return 'gc_new_floatarray(' @@ -438,7 +443,7 @@ export function arrayFromSize(arrayType: ArrayType, env: VariableEnv) { export function arrayFromArray(arrayType: ArrayType, env: VariableEnv) { const t = arrayType.elementType - if (t === Integer || t instanceof EnumType) + if (isIntegerLike(t)) return 'gc_copy_intarray(' else if (t === Float) return 'gc_copy_floatarray(' @@ -453,7 +458,9 @@ export function arrayFromArray(arrayType: ArrayType, env: VariableEnv) { export const prefixOfarrayTypeNameInC = 'array_type' export function actualElementType(t: StaticType) { - if (t === Integer || t instanceof EnumType) + if (t === Int32) + return Int32 + else if (t === Integer || t instanceof EnumType) return Integer else if (t === Float) return Float diff --git a/lang/src/transpiler/code-generator/code-generator.ts b/lang/src/transpiler/code-generator/code-generator.ts index 6ec2efb3..2595d0b7 100644 --- a/lang/src/transpiler/code-generator/code-generator.ts +++ b/lang/src/transpiler/code-generator/code-generator.ts @@ -2,7 +2,7 @@ import * as AST from '@babel/types' import { runBabelParser, ErrorLog, CodeWriter } from '../utils' -import { Integer, BooleanT, Void, Any, ObjectType, FunctionType, +import { Integer, Int32, BooleanT, Void, Any, ObjectType, FunctionType, StaticType, ByteArrayClass, isPrimitiveType, encodeType, sameType, typeToString, ArrayType, objectType, StringT, UnionType, FixedArrayClass, StringType, EnumType} from '../types' @@ -1089,7 +1089,15 @@ export class CodeGenerator extends visitor.NodeVisitor { const left_type = this.needsCoercion(left) const right_type = this.needsCoercion(right) - if ((left_type === BooleanT || right_type === BooleanT) + if (left_type === Int32 && right_type === Any || left_type === Any && right_type === Int32) { + this.result.write(`${cr.typeConversion(left_type, Int32, env, left)}`) + this.visit(left, env) + this.result.write(`) ${op} `) + this.result.write(`${cr.typeConversion(right_type, Int32, env, right)}`) + this.visit(right, env) + this.result.write(')') + } + else if ((left_type === BooleanT || right_type === BooleanT) // if either left or right operand is boolean, the other is boolean || (left_type === StringT || right_type === StringT) || (left_type === Any || right_type === Any) @@ -1108,7 +1116,15 @@ export class CodeGenerator extends visitor.NodeVisitor { private basicBinaryExpression(op: string, node: AST.BinaryExpression, left: AST.Node, right: AST.Node, env: VariableEnv): void { const left_type = this.needsCoercion(left) const right_type = this.needsCoercion(right) - if (left_type === Any || right_type === Any || left_type === StringT || right_type === StringT) { + if (left_type === Int32 && right_type === Any || left_type === Any && right_type === Int32) { + this.result.write(`${cr.typeConversion(left_type, Int32, env, left)}`) + this.visit(left, env) + this.result.write(`) ${op} `) + this.result.write(`${cr.typeConversion(right_type, Int32, env, right)}`) + this.visit(right, env) + this.result.write(')') + } + else if (left_type === Any || right_type === Any || left_type === StringT || right_type === StringT) { this.result.write(`${cr.arithmeticOpForAny(op)}(${cr.typeConversion(left_type, Any, env, left)}`) this.visit(left, env) this.result.write(`), ${cr.typeConversion(right_type, Any, env, right)}`) diff --git a/lang/src/transpiler/type-checker.ts b/lang/src/transpiler/type-checker.ts index 3d565a59..feef5b91 100644 --- a/lang/src/transpiler/type-checker.ts +++ b/lang/src/transpiler/type-checker.ts @@ -7,10 +7,10 @@ import * as visitor from './visitor' import { ArrayType, StaticType, ByteArrayClass, isPrimitiveType, UnionType, FixedArrayClass, StringType } from './types' import { - Integer, Float, BooleanT, StringT, Void, Null, Any, + Integer, Int32, Float, BooleanT, StringT, Void, Null, Any, EnumType, ObjectType, FunctionType, objectType, typeToString, isSubtype, isConsistent, commonSuperType, - isNumeric, isEnum, isBuiltinTypeName + isNumeric, isIntegerType, isIntegerLike, isEnum, isBuiltinTypeName } from './types' import { actualElementType } from './code-generator/c-runtime' @@ -460,14 +460,15 @@ export default class TypeChecker extends visitor.NodeVisi this.result = clazz this.visit(node.body, names) - clazz.sortProperties() + if (!clazz.sortProperties()) + clazz.forEach((name, type, __) => this.assert(type !== Int32, `this subclass ${className} cannot contain an Int32 property '${name}'`, node)) if (!clazz.findConstructor()) { // this class has a default constructor. - this.assert(clazz.declaredProperties() === 0, 'a constructor is missing', node) + this.assert(clazz.declaredProperties() === 0, `${className} is missing a constructor`, node) if (superClass instanceof InstanceType) { const cons = superClass.findConstructor() - this.assert(!cons || cons.paramTypes.length === 0, 'a constructor is missing', node) + this.assert(!cons || cons.paramTypes.length === 0, `${className} is missing a constructor`, node) } } @@ -690,8 +691,14 @@ export default class TypeChecker extends visitor.NodeVisi this.assert(this.result !== Void, 'void may not be an initial value', init) if (varType === undefined) varType = this.result - else if (isConsistent(this.result, varType)) + else if (isConsistent(this.result, varType) || varType === Int32 && this.result === Any) { + // let a: initeger[] = [1, 'one'] is not allowed. + if (AST.isArrayExpression(init) && varType instanceof ArrayType && this.result instanceof ArrayType + && !isConsistent(this.result.elementType, varType.elementType)) + this.assert(false, 'bad array initializer: element type is not consistent with the variable type', init) + this.addCoercion(init, this.result) + } else this.assert(isSubtype(this.result, varType), `Type '${typeToString(this.result)}' is not assignable to type '${typeToString(varType)}'`, id) @@ -876,7 +883,7 @@ export default class TypeChecker extends visitor.NodeVisi else if (op === '~') { // this.result must be integer or any-type. // It must not be an array type or a function type. - this.assert(this.result === Integer || this.result === Any || isEnum(this.result), + this.assert(isIntegerLike(this.result) || this.result === Any, this.invalidOperandMessage(op, this.result), node) this.result = Integer } @@ -953,6 +960,7 @@ export default class TypeChecker extends visitor.NodeVisi } else if (op === '+' || op === '-' || op === '*' || op === '/' || op === '**') { if (op === '+' && (left_type === StringT || right_type === StringT)) { + this.assert(left_type !== Int32 && right_type !== Int32, this.invalidOperandsMessage(op, left_type, right_type), node) this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) this.result = StringT @@ -963,7 +971,7 @@ export default class TypeChecker extends visitor.NodeVisi if (left_type === Any || right_type === Any) { this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) - this.result = Any + this.result = left_type === Int32 || right_type === Int32 ? Int32 : Any } else if (left_type === Float || right_type === Float) { if (op === '**') @@ -972,28 +980,29 @@ export default class TypeChecker extends visitor.NodeVisi this.result = Float } else { + const type = (left_type === Int32 || right_type === Int32) ? Int32 : Integer if (op === '**') - this.addStaticType(node, Integer) + this.addStaticType(node, type) - this.result = Integer + this.result = type } } } else if (op === '%') { - this.assert((left_type === Integer || left_type === Any) && (right_type === Integer || right_type === Any), + this.assert((isIntegerLike(left_type) || left_type === Any) && (isIntegerLike(right_type) || right_type === Any), 'invalid operands to %. They must be integer or any', node) if (left_type === Any || right_type === Any) { this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) - this.result = Any + this.result = (left_type === Int32 || right_type === Int32) ? Int32 : Any } else - this.result = Integer + this.result = (left_type === Int32 || right_type === Int32) ? Int32 : Integer } else if (op === '|' || op === '^' || op === '&' || op === '<<' || op === '>>' || op === '>>>') { - this.assert(this.firstPass || (left_type === Integer || isEnum(left_type)) && (right_type === Integer || isEnum(right_type)), + this.assert(this.firstPass || isIntegerLike(left_type) && isIntegerLike(right_type), this.invalidOperandsMessage(op, left_type, right_type), node) - this.result = Integer + this.result = (left_type === Int32 || right_type === Int32) ? Int32 : Integer } else { // 'in', '|>' this.assert(false, `not supported operator '${op}'`, node) @@ -1047,7 +1056,8 @@ export default class TypeChecker extends visitor.NodeVisi const op = node.operator if (op === '=') { - if (isConsistent(right_type, left_type) || this.isConsistentOnFirstPass(right_type, left_type)) { + if (isConsistent(right_type, left_type) || (left_type === Int32 && right_type === Any) + || this.isConsistentOnFirstPass(right_type, left_type)) { this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) } @@ -1060,27 +1070,30 @@ export default class TypeChecker extends visitor.NodeVisi } } else if (op === '+=' && (left_type === StringT || left_type === Any)) { - this.addCoercion(node.left, StringT) + this.assert(right_type !== Int32, 'invalid operands to +=. The right operand cannot be Int32', node) + this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) } else if (op === '+=' || op === '-=' || op === '*=' || op === '/=') { - this.assert((isNumeric(left_type) || left_type === Any) && (isNumeric(right_type) || right_type === Any), - this.invalidOperandsMessage(op, left_type, right_type), node) + const errmsg = this.invalidOperandsMessage(op, left_type, right_type) + this.assert((isIntegerType(left_type) || left_type === Float || left_type === Any) && (isNumeric(right_type) || right_type === Any), errmsg, node) + this.assert(left_type !== Any || right_type !== Int32, errmsg, node) if (left_type === Any || right_type === Any) { // "if (isConsistent(...))" is wrong this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) } } else if (op === '%=') { - this.assert((left_type === Integer || left_type === Any) && (right_type === Integer || right_type === Any), - 'invalid operands to %=. They must be integer or any', node) + const errmsg = 'invalid operands to %=' + this.assert((isIntegerType(left_type) || left_type === Any) && (isIntegerLike(right_type) || right_type === Any), errmsg, node) + this.assert(left_type !== Any || right_type !== Int32, errmsg, node) if (left_type === Any || right_type === Any) { this.addCoercion(node.left, left_type) this.addCoercion(node.right, right_type) } } else if (op === '|=' || op === '^=' || op === '&=' || op === '%=' || op === '<<=' || op === '>>=') - this.assert(left_type === Integer && (right_type === Integer || isEnum(right_type)), + this.assert(isIntegerType(left_type) && isIntegerLike(right_type), this.invalidOperandsMessage(op, left_type, right_type), node) else // '||=', '&&=', '>>>=', '**=', op === '??=' this.assert(false, `not supported operator '${op}'`, node) @@ -1132,7 +1145,7 @@ export default class TypeChecker extends visitor.NodeVisi actualType = elementType if (op === '=') - if (isConsistent(rightType, elementType)) { + if (isConsistent(rightType, elementType) || elementType === Int32 && rightType === Any) { this.addCoercion(node.left, actualType) this.addCoercion(node.right, rightType) } @@ -1148,8 +1161,9 @@ export default class TypeChecker extends visitor.NodeVisi this.addCoercion(node.right, rightType) } else if (op === '+=' || op === '-=' || op === '*=' || op === '/=') { - this.assert((isNumeric(elementType) || elementType === Any) && (isNumeric(rightType) || rightType === Any), - this.invalidOperandsMessage(op, elementType, rightType), node) + const errmsg = this.invalidOperandsMessage(op, elementType, rightType) + this.assert((isIntegerType(elementType) || elementType === Float || elementType === Any) && (isNumeric(rightType) || rightType === Any), errmsg, node) + this.assert(elementType !== Any || rightType !== Int32, errmsg, node) this.addCoercion(node.left, actualType) this.addCoercion(node.right, rightType) } @@ -1243,7 +1257,8 @@ export default class TypeChecker extends visitor.NodeVisi this.visit(arg, names) const argType = this.result for (const t of paramTypes) - if (isConsistent(argType, t) || this.isConsistentOnFirstPass(argType, t) || isSubtype(argType, t)) { + if (isConsistent(argType, t) || this.isConsistentOnFirstPass(argType, t) || isSubtype(argType, t) + || argType === Any && t === Int32) { this.addStaticType(arg, argType) return } @@ -1258,7 +1273,8 @@ export default class TypeChecker extends visitor.NodeVisi this.visit(arg, names) const argType = this.result if (isConsistent(argType, paramType) || this.isConsistentOnFirstPass(argType, paramType) - || isSubtype(argType, paramType) || argType instanceof ArrayType) { + || isSubtype(argType, paramType) || argType instanceof ArrayType + || argType === Any /* || argType === Any && paramType === Int32 */ ) { // argType === Any checks whether the arugument may be an array this.addStaticType(arg, argType) return } @@ -1316,7 +1332,8 @@ export default class TypeChecker extends visitor.NodeVisi } private newArrayExpression(node: AST.NewExpression, names: NameTable): void { - const typeParams = node.typeParameters?.params?.map(e => { + const typeParamNodes = node.typeParameters?.params as AST.TSType[] | undefined + const typeParams = typeParamNodes?.map((e: AST.TSType) => { this.visit(e, names) return this.result }) @@ -1334,10 +1351,10 @@ export default class TypeChecker extends visitor.NodeVisi this.callExpressionArg(args[1], etype, names) } else if (this.assert(args.length === 1, 'wrong number of arguments', node)) { - // new Array(p: Integer|T[]|any[]) + // new Array(p: Integer|Int32|T[]|any[]) if (AST.isArrayExpression(args[0])) this.arrayExpressionWithUpperType(args[0], names, etype === Any ? undefined : etype) - else if (etype === Integer || etype === Float || etype === BooleanT || isEnum(etype)) + else if (isIntegerType(etype) || etype === Float || etype === BooleanT || isEnum(etype)) this.callExpressionOLArg(args[0], [Integer, atype, new ArrayType(Any)], names) else if (etype === Any) this.callExpressionArgOrArray(args[0], Integer, names) @@ -1403,8 +1420,10 @@ export default class TypeChecker extends visitor.NodeVisi etype = this.result else { const t = commonSuperType(etype, this.result) - if (t === undefined) + if (t === undefined) { + this.assert(etype !== Int32 && this.result !== Int32, 'array element types are not compatible', ele) etype = Any + } else etype = t } @@ -1452,7 +1471,7 @@ export default class TypeChecker extends visitor.NodeVisi private checkArrayAccessExpr(node: AST.MemberExpression, readonly: boolean, names: NameTable) { this.assert(AST.isExpression(node.property), 'a wrong index expression', node.property) this.visit(node.property, names) - this.assert(this.firstPass || this.result === Integer || this.result === Any, + this.assert(this.firstPass || isIntegerType(this.result) || this.result === Any, 'an array index must be an integer', node.property) this.addCoercionIfAny(node.property, this.result) this.visit(node.object, names) @@ -1671,6 +1690,8 @@ export default class TypeChecker extends visitor.NodeVisi this.result = Float else if (name === Integer) this.result = Integer + else if (name === Int32) + this.result = Int32 else { const nameInfo = names.lookup(name) if (nameInfo === undefined) @@ -1764,7 +1785,7 @@ export default class TypeChecker extends visitor.NodeVisi tsTypeAliasDeclaration(node: AST.TSTypeAliasDeclaration, env: NameTable): void { const name = node.id.name - this.assert(name === 'integer' || name === 'float', 'type alias is not supported', node) + this.assert(name === Integer || name === Int32 || name === Float, 'type alias is not supported', node) } exportNamedDeclaration(node: AST.ExportNamedDeclaration, env: NameTable): void { diff --git a/lang/src/transpiler/types.ts b/lang/src/transpiler/types.ts index a4a59db0..023087cd 100644 --- a/lang/src/transpiler/types.ts +++ b/lang/src/transpiler/types.ts @@ -1,6 +1,7 @@ // Copyright (C) 2023- Shigeru Chiba. All rights reserved. export const Integer = 'integer' +export const Int32 = 'int32' export const Float = 'float' export const BooleanT = 'boolean' export const StringT = 'string' @@ -11,21 +12,29 @@ export const Any = 'any' export const ByteArrayClass = 'Uint8Array' // Uint8Array is also used as byte[]. export const FixedArrayClass = 'FixedArray' -export type StaticType = 'integer' | 'float' | 'boolean' | 'string' | 'void' | 'null' | 'any' | +export type StaticType = 'integer' | 'int32' | 'float' | 'boolean' | 'string' | 'void' | 'null' | 'any' | ObjectType | FunctionType | UnionType | EnumType export function isBuiltinTypeName(name: string) { - return name === Integer || name === Float || name === BooleanT || name === Void || name === StringT || name === Null || + return name === Integer || name === Int32 || name === Float || name === BooleanT || name === Void || name === StringT || name === Null || name === Any || name === 'undefined' || name === 'Array' || name === 'object' } export function isPrimitiveType(type: StaticType) { // unless String, Null, FunctionType, Any, or object type - return type === Integer || type === Float || type === BooleanT || type === Void || (type instanceof EnumType) + return type === Integer || type === Int32 || type === Float || type === BooleanT || type === Void || (type instanceof EnumType) +} + +export function isIntegerType(t: StaticType) { + return t === Integer || t === Int32 +} + +export function isIntegerLike(t: StaticType) { + return isIntegerType(t) || (t instanceof EnumType) } export function isNumeric(t: StaticType) { - return t === Integer || t === Float || (t instanceof EnumType) + return t === Integer || t === Int32 || t === Float || (t instanceof EnumType) } export function isEnum(t: StaticType) { @@ -298,6 +307,7 @@ export function typeToString(type: StaticType): string { export function encodeType(type: StaticType): string { switch (type) { case Integer: + case Int32: return 'i' case Float: return 'f' @@ -337,15 +347,16 @@ export function isSubtype(subtype: StaticType, type: StaticType): boolean { return subtype.isSubtypeOf(type) else if (type instanceof UnionType) return type.isSuperTypeOf(subtype) - else if (subtype === Integer && type === Float - || subtype === StringT && type === objectType) - return true - else if (type === BooleanT) - return subtype === BooleanT + else if (isIntegerType(subtype)) + return isIntegerType(type) || type === Float + else if (subtype === StringT) + return type === objectType + else if (subtype === BooleanT) + return type === BooleanT else if (subtype instanceof CompositeType) return subtype.isSubtypeOf(type) else if (subtype instanceof EnumType) - return type === Integer || type === Float + return isIntegerType(type) || type === Float else return false } @@ -363,18 +374,22 @@ export function sameType(t1: StaticType, t2: StaticType) { // after explicit conversion. That conversion may throw a runtime type error. export function isConsistent(t1: StaticType, t2: StaticType) { if (t1 === Any) - return t1 !== t2 && t2 !== Void + return t2 !== Any && t2 !== Void && t2 !== Int32 else if (t2 === Any) - return t1 !== Void + return (t1 !== Void && t1 !== Int32) else if (t1 instanceof ArrayType && t2 instanceof ArrayType) - return isConsistent(t1.elementType, t2.elementType) + if (isConsistent(t1.elementType, t2.elementType)) + return true + else + return t1.elementType === Int32 && t2.elementType === Any + || t1.elementType === Any && t2.elementType === Int32 else return false } export function commonSuperType(t1: StaticType, t2: StaticType): StaticType | undefined { - if (t1 === Float && t2 === Integer) - return Float + if ((t1 === Integer && t2 === Int32) || (t1 === Int32 && t2 === Integer)) + return Int32 else if (isSubtype(t1, t2)) return t2 else if (isSubtype(t2, t1)) diff --git a/lang/tests/transpiler/code-generator/code-generator.test.ts b/lang/tests/transpiler/code-generator/code-generator.test.ts index 59cadb15..2ef374fc 100644 --- a/lang/tests/transpiler/code-generator/code-generator.test.ts +++ b/lang/tests/transpiler/code-generator/code-generator.test.ts @@ -1115,6 +1115,111 @@ test('bad value assignment to a string array', () => { expect(() => { compileAndRun(src) }).toThrow(/not assignable to element type/) }) +test('int32 computing', () => { + const src = ` + function foo(n: int32): int32 { + let i = n + 1 + print(typeof i) + let j: any = 2 + i++ + --i + i += j + i -= j + let k: int32 = i * j + let m: int32 = j + i + return m + } + + print_i32(foo(0x7fffffff - 2)) + print_i32(foo(5)) + ` + + expect(compileAndRun(src)).toBe('int32\n-2147483648\nint32\n8\n') +}) + +test('int32-type array is compatible with any-type array', () => { + const src = ` + function sum(arr: any[]) { + return arr[0] + arr[1] + } + + const ints: int32[] = new Array([1, 2, 3]) + const values: any[] = ints + print(values.length) + print(values[0]) + print_i32(ints[0]) + print(sum(ints)) + ` + + expect(compileAndRun(src)).toBe('3\n1\n1\n3\n') +}) + +test('a subclas contains int32 property', () => { + const src = ` + class A { + a: int32 + i: integer + constructor() { + this.a = 0 + this.i = 1 + } + } + + class B extends A { + b: int32 + constructor() { + super() + this.b = 2 + } + } + + const b = new B() + print_i32(b.a) + print_i32(b.b) + ` + + expect(compileAndRun(src)).toBe('0\n2\n') +}) + +test('a subclas may not contain int32 property', () => { + const src = ` + class A { + a: integer + s: string + constructor() { + this.a = 3 + this.s = 'foo' + } + } + + class B extends A { + b: int32 + constructor() { + super() + this.b = 2 + } + } + ` + + expect(() => { compileAndRun(src) }).toThrow(/cannot contain an Int32 property/) +}) + +test('An int32 value is captured in a closure', () => { + const src = ` + function foo() { + const i: int32 = 0x7fffffff + const f: integer = 123 + return () => { return i + 1 } + } + + print(typeof foo) + const f = foo() + print_i32(f()) + ` + + expect(compileAndRun(src)).toBe('() => () => int32\n-2147483648\n') +}) + test('any-type array', () => { const src = ` function foo(n: integer): integer { @@ -1872,10 +1977,10 @@ test('class with a bad constructor', () => { } ` - expect(() => { compileAndRun(src)}).toThrow(/constructor is missing/) + expect(() => { compileAndRun(src)}).toThrow(/missing a constructor/) expect(() => { compileAndRun(src1)}).toThrow(/uninitialized property: y/) expect(() => { compileAndRun(src2)}).toThrow(/super\(\).*only valid inside a class constructor of a subclass.*not extending another class/) - expect(() => { compileAndRun(src3)}).toThrow(/constructor is missing/) + expect(() => { compileAndRun(src3)}).toThrow(/missing a constructor/) expect(compileAndRun(src4)).toBe('8\n') expect(compileAndRun(src5)).toBe('4\n') expect(() => { compileAndRun(src6)}).toThrow(/super\(\) is not called/) diff --git a/lang/tests/transpiler/type-checker.test.ts b/lang/tests/transpiler/type-checker.test.ts index 8d7f1704..f2ac8a2a 100644 --- a/lang/tests/transpiler/type-checker.test.ts +++ b/lang/tests/transpiler/type-checker.test.ts @@ -58,6 +58,39 @@ test('bad type declaraton', () => { expect(() => tested.transpile(src)).toThrow(/line 1.*\n.*line 3/) }) +test('int32 type declaration', () => { + const src = `const i: integer = 3 + const j: int32 = i + const k: integer = j + const a: int32[] = new Array([1, 2, 3]) + const b: any[] = a` + const ast = tested.transpile(src) + const table = names.getNameTable(ast.program) + expect(table?.lookup('j')?.type).toBe(types.Int32) + expect(table?.lookup('k')?.type).toBe(types.Integer) + expect((table?.lookup('a')?.type as types.ArrayType).elementType).toBe(types.Int32) + expect((table?.lookup('b')?.type as types.ArrayType).elementType).toBe(types.Any) +}) + +test('int32 is not compatible with any', () => { + const src = `const a: int32 = 3 + const b: any = a` + expect(() => tested.transpile(src)).toThrow(/not assignable to type 'any'/) + + const src2 = `const a: any = 3 + const b: int32 = a + let c: int32 = a + 1 + c = a` + expect(tested.transpile(src2)).not.toBeNull() + + const src3 = `const a: int32 = 3 + const b: any = 4 + const c = a + b` + const ast = tested.transpile(src3) + const table = names.getNameTable(ast.program) + expect(table?.lookup('c')?.type).toBe(types.Int32) +}) + test('const declaraton', () => { const src = `const k = 3 k = 1` diff --git a/website/docs/reference/language/built-in-objects.md b/website/docs/reference/language/built-in-objects.md index 1bb21531..b6eeccbc 100644 --- a/website/docs/reference/language/built-in-objects.md +++ b/website/docs/reference/language/built-in-objects.md @@ -54,12 +54,13 @@ are identified by an integer index. ### Array Objects -BlueScript currently supports arrays of `integer`, `float`, `boolean`, `string`, class types, -array types, and `any`-type. +BlueScript currently supports arrays of `integer`, `int32`, `float`, `boolean`, `string`, class types, +array types, and the `any` type. Their names are `T[]`, where `T` is an element type. -Array types can be implicitly converted into `any`-type, and vice versa. -In other words, a reference to an array of `integer`, `float`, etc. is implicitly converted into an `any`-type value. +Array types can be implicitly converted into the `any` type, and vice versa. +In other words, a reference to an array of `integer`, `float`, etc. is implicitly converted +into an value of the `any` type (i.e. an `any`-type value). An `any`-type value is also implicitly converted into a reference to an array if the `any`-type value points to an array object of that array type. Otherwise, a runtime error is thrown. @@ -70,7 +71,8 @@ let a: any = iarr let i: integer = a[0] // a[0] is an `any`-type value although iarr[0] is an integer ``` -An array type is invariant. For example, `A[]` is not a subtype of `B[]` or its super type even when the type `A` is a subtype of `B`. +An array type is invariant. For example, `A[]` is not a subtype of `B[]` or its super type +even when the type `A` is a subtype of `B`. However, an array type may be implicitly converted into `any[]`. ```tsx @@ -80,7 +82,7 @@ let i: integer = a[0] // a[0] is an `any`-type value a[0] = 'one' // runtime error. The array can contain only integers. ``` -Arrays can be constructed to contain values of `any`-type. +Arrays can be constructed to contain values of the `any` type. Their types are `any[]`. ```tsx @@ -115,19 +117,21 @@ let arr = [1, 2.0, 'three'] // any[] An array object is created by `new Array(size, value)`. Here, `T` is a meta variable representing a type name. `size` is the number of the array elements. `value` is the initial value for the array elements. -`T` can be `integer`, `float`, `boolean`, `string`, an array type, a class type, or `any`-type. +`T` can be `integer`, `int32`, `float`, `boolean`, `string`, an array type, a class type, or the `any` type. ```tsx let iarr = new Array(3, 0); ``` -When the element type is `integer`, `float`, `boolean`, or `any`, +When the element type is `integer`, `int32`, `float`, `boolean`, or `any`, the second argument to the `Array` constructor can be omitted. The initial values are zero, `false`, or `undefined`. For example, `new Array(7)` is a valid expression, and it constructs an array including 7 elements. -The constructor for the `Array` type provides two other overloads: one accepting an array of type `T[]`, and another accepting an array of type `any[]`. This is particularly convenient when initializing an array using a variable of type `any`." +The constructor for the `Array` type provides two other overloads: one accepting a value of an array type `T[]`, +and another accepting a value of an array type `any[]`. The latter is particularly convenient +when initializing an array using a variable of the `any` type. ```tsx let three: any = 3 diff --git a/website/docs/reference/language/intro.md b/website/docs/reference/language/intro.md index b62321e8..71421e23 100644 --- a/website/docs/reference/language/intro.md +++ b/website/docs/reference/language/intro.md @@ -5,10 +5,11 @@ It borrows its syntax from TypeScript and hence it is regarded as a small subset of TypeScript. But BlueScript also adopts several unique semantic differences from TypeScript. -BlueScript is a subset of TypeScript because, for example, it (*currently*) does not support exceptions, promises, async/await, or certain built-in objects. +BlueScript is a subset of TypeScript because, for example, it (*currently*) does not support exceptions, +promises, async/await, or certain built-in objects. On the other hand, unlike TypeScript, BlueScript supports `integer` and `float` primitive types, and a BlueScript program is executed relying on static type information. When a variable is statically typed as `integer`, -it always holds a native 32bit integer during runtime +it always holds a native integer-value during runtime as the C/C++ language does. It never holds another type of value, and thus no runtime type check is necessary. Furthermore, BlueScript supports simple gradual typing. diff --git a/website/docs/reference/language/primitive-types.md b/website/docs/reference/language/primitive-types.md index 6f52a037..2d900d63 100644 --- a/website/docs/reference/language/primitive-types.md +++ b/website/docs/reference/language/primitive-types.md @@ -2,17 +2,20 @@ BlueScript provides six primitive types as well as object types: -- `integer` (32bit integer) +- `integer` (30-bit integer) +- `int32` (32-bit integer) - `number` (an alias of `integer`) -- `float` (32bit floating-point number) +- `float` (30bit floating-point number) - `string` - `boolean` - `null` and `undefined` (they are the same) - `any` -Any kind of value can be implicitly converted into `any` type, and vice versa. - - When an `integer` value is converted, the resulting value is represented as a 30bit integer. - - When a `float` value is converted, the resulting value is represented as a 30bit floating-point number, +Any kind of value can be implicitly converted into the `any` type, and vice versa. + - Before being converted into the `any` type, an `integer` value may be stored as a 32-bit integer. + - An `int32` value is always stored as a 32-bit integer. It is never converted into the `any` type. + - Before being converted into the `any` type, an `float` value may be stored as a 32-bit floating-point number. + After being converted into the `any` type, it is stored as a 30-bit floating-point number, where only 6 bits are allocated for an exponent instead of 8 bits. - For logical operations and the condition expressions of coditional/loop statements such as `if` and `while`, From 4a88c79e1e56053a4b958c591c53a45650b7a364 Mon Sep 17 00:00:00 2001 From: chibash Date: Sat, 15 Aug 2026 00:44:17 +0900 Subject: [PATCH 5/6] replaces fixarray with fixedarray --- .../transpiler/code-generator/c-runtime.ts | 6 +- .../code-generator/code-generator.ts | 4 +- microcontroller/core/include/c-runtime.h | 12 +-- microcontroller/core/src/c-runtime.c | 58 ++++++------ microcontroller/core/test/c-runtime-test.c | 88 +++++++++---------- microcontroller/core/test/c-runtime-test2.c | 28 +++--- 6 files changed, 98 insertions(+), 98 deletions(-) diff --git a/lang/src/transpiler/code-generator/c-runtime.ts b/lang/src/transpiler/code-generator/c-runtime.ts index bc6f4feb..16e9d0dc 100644 --- a/lang/src/transpiler/code-generator/c-runtime.ts +++ b/lang/src/transpiler/code-generator/c-runtime.ts @@ -386,7 +386,7 @@ export function arrayElementGetter(t: StaticType | undefined, arrayType: StaticT if (arrayType instanceof InstanceType && arrayType.name() === ByteArrayClass) return '(*gc_bytearray_get(' else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) - return '(gc_fixarray_get(' + return '(gc_fixedarray_get(' else if (arrayType === Any) return '(gc_safe_array_get(' else if (t === Integer || t === Int32 || t instanceof EnumType) @@ -405,7 +405,7 @@ export function arrayElementSetter(arrayType: StaticType | undefined) { else if (arrayType === Any) return 'gc_safe_array_set(' else if (arrayType instanceof InstanceType && arrayType.name() === FixedArrayClass) - return 'gc_fixarray_set(' + return 'gc_fixedarray_set(' else return `gc_safe_array_set(` } @@ -603,7 +603,7 @@ export function makeInstance(clazz: InstanceType, func: () => string) { if (name === ByteArrayClass) return 'gc_new_bytearray(false' else if (name === FixedArrayClass) - return 'gc_new_fixarray(' + return 'gc_new_fixedarray(' else return `${constructorNameInC(name)}(${func()}gc_new_object(&${classObjectNameInC(name)})` } diff --git a/lang/src/transpiler/code-generator/code-generator.ts b/lang/src/transpiler/code-generator/code-generator.ts index 2595d0b7..a460b77c 100644 --- a/lang/src/transpiler/code-generator/code-generator.ts +++ b/lang/src/transpiler/code-generator/code-generator.ts @@ -228,9 +228,9 @@ export class CodeGenerator extends visitor.NodeVisitor { }) if (frees.length > 0) { - // all the arguments to gc_make_fixarray must be reachable + // all the arguments to gc_make_fixedarray must be reachable // from the garbage-collection root. - obj = `gc_make_fixarray(${frees.length}${args})` + obj = `gc_make_fixedarray(${frees.length}${args})` } } diff --git a/microcontroller/core/include/c-runtime.h b/microcontroller/core/include/c-runtime.h index df6ec1ac..e0e103b7 100644 --- a/microcontroller/core/include/c-runtime.h +++ b/microcontroller/core/include/c-runtime.h @@ -260,12 +260,12 @@ extern int32_t CR_SECTION gc_bytearray_length(value_t obj); extern uint8_t* CR_SECTION gc_bytearray_get(value_t obj, int32_t index); extern bool CR_SECTION gc_is_boolarray(value_t v); -extern value_t CR_SECTION safe_value_to_fixarray(bool nullable, value_t v); -extern value_t CR_SECTION gc_new_fixarray(int32_t n, value_t init_value); -extern int32_t CR_SECTION gc_fixarray_length(value_t obj); -extern value_t CR_SECTION gc_fixarray_get(value_t obj, int32_t index); -extern value_t CR_SECTION gc_fixarray_set(value_t obj, int32_t index, value_t new_value); -extern value_t CR_SECTION gc_make_fixarray(int32_t n, ...); +extern value_t CR_SECTION safe_value_to_fixedarray(bool nullable, value_t v); +extern value_t CR_SECTION gc_new_fixedarray(int32_t n, value_t init_value); +extern int32_t CR_SECTION gc_fixedarray_length(value_t obj); +extern value_t CR_SECTION gc_fixedarray_get(value_t obj, int32_t index); +extern value_t CR_SECTION gc_fixedarray_set(value_t obj, int32_t index, value_t new_value); +extern value_t CR_SECTION gc_make_fixedarray(int32_t n, ...); extern bool CR_SECTION gc_is_instance_of_array(value_t obj); extern value_t CR_SECTION safe_value_to_anyarray(bool nullable, value_t v); diff --git a/microcontroller/core/src/c-runtime.c b/microcontroller/core/src/c-runtime.c index 5ce3b3f3..f5b1626e 100644 --- a/microcontroller/core/src/c-runtime.c +++ b/microcontroller/core/src/c-runtime.c @@ -15,9 +15,9 @@ root_set.values[0] = a; root_set.values[1] = b; - root_set.values[2] = obj = gc_new_fixarray(2); - gc_fixarray_set(obj, int_to_value(0), a); - gc_fixarray_set(obj, int_to_value(1), b); + root_set.values[2] = obj = gc_new_fixedarray(2); + gc_fixedarray_set(obj, int_to_value(0), a); + gc_fixedarray_set(obj, int_to_value(1), b); DELETE_ROOT_SET(root_set); return obj; @@ -709,7 +709,7 @@ const void* gc_function_object_ptr(value_t obj, int index) { // returns an instance of boxed_value or boxed_raw_value, returned by gc_new_box() etc. value_t gc_function_captured_value(value_t obj, int index) { value_t vec = value_to_ptr(obj)->body[2]; - return gc_fixarray_get(vec, index); + return gc_fixedarray_get(vec, index); } // boxed_value and boxed_raw_value are classes for boxing. Their instances hold one value_t value @@ -1343,17 +1343,17 @@ CLASS_OBJECT(class_FixedArray, 1) = { .clazz = { .size = -1, .start_index = 1, .name = "FixedArray", .superclass = &object_class.clazz, .array_type_name = "'FixedArray'", .table = DEFAULT_PTABLE, .mtable = DEFAULT_MTABLE }}; -value_t safe_value_to_fixarray(bool nullable, value_t v) { +value_t safe_value_to_fixedarray(bool nullable, value_t v) { return safe_value_to_value(nullable, &class_FixedArray.clazz, v); } /* - A fixed-length array. We call it a fixarray. + A fixed-length array. We call it a fixedarray. n: the number of array elements. 1st word is the number of elements. 2nd, 3rd, ... words hold elements. */ -value_t gc_new_fixarray(int32_t n, value_t init_value) { +value_t gc_new_fixedarray(int32_t n, value_t init_value) { ROOT_SET(rootset, 1) rootset.values[0] = init_value; if (n < 0) @@ -1369,21 +1369,21 @@ value_t gc_new_fixarray(int32_t n, value_t init_value) { return ptr_to_value(obj); } -inline static value_t* fast_fixarray_get(value_t obj, int32_t index) { +inline static value_t* fast_fixedarray_get(value_t obj, int32_t index) { pointer_t objp = value_to_ptr(obj); return &objp->body[index + 1]; } -inline static void fast_fixarray_set(value_t obj, uint32_t index, value_t new_value) { +inline static void fast_fixedarray_set(value_t obj, uint32_t index, value_t new_value) { pointer_t objp = value_to_ptr(obj); gc_write_barrier(objp, new_value); objp->body[index + 1] = new_value; } -static value_t duplicate_fixarray(int32_t n, int32_t offset, value_t vec) { +static value_t duplicate_fixedarray(int32_t n, int32_t offset, value_t vec) { ROOT_SET(rootset, 1) rootset.values[0] = vec; - int32_t len = gc_fixarray_length(vec); + int32_t len = gc_fixedarray_length(vec); if (n < 0) n = 0; else if (n < len + offset) @@ -1405,12 +1405,12 @@ static value_t duplicate_fixarray(int32_t n, int32_t offset, value_t vec) { return ptr_to_value(obj); } -int32_t gc_fixarray_length(value_t obj) { +int32_t gc_fixedarray_length(value_t obj) { pointer_t objp = value_to_ptr(obj); return objp->body[0]; } -value_t gc_fixarray_get(value_t obj, int32_t idx) { +value_t gc_fixedarray_get(value_t obj, int32_t idx) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= idx && idx < len) @@ -1421,7 +1421,7 @@ value_t gc_fixarray_get(value_t obj, int32_t idx) { } } -value_t gc_fixarray_set(value_t obj, int32_t index, value_t new_value) { +value_t gc_fixedarray_set(value_t obj, int32_t index, value_t new_value) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= index && index < len) { @@ -1439,12 +1439,12 @@ value_t gc_fixarray_set(value_t obj, int32_t index, value_t new_value) { A caller function must guarantee that they are reachable from the root. */ -value_t gc_make_fixarray(int32_t n, ...) { +value_t gc_make_fixedarray(int32_t n, ...) { va_list args; - value_t array = gc_new_fixarray(n, VALUE_UNDEF); + value_t array = gc_new_fixedarray(n, VALUE_UNDEF); va_start(args, n); for (int32_t i = 0; i < n; i++) - fast_fixarray_set(array, i, va_arg(args, value_t)); + fast_fixedarray_set(array, i, va_arg(args, value_t)); va_end(args); return array; } @@ -1494,7 +1494,7 @@ value_t gc_new_array(const class_object* clazz, int32_t n, value_t init_value) { pointer_t obj = gc_allocate_object(clazz == NULL ? &anyarray_object.clazz : clazz); rootset.values[1] = ptr_to_value(obj); const int32_t size = real_array_length(n); - value_t arr = gc_new_fixarray(size, init_value); + value_t arr = gc_new_fixedarray(size, init_value); pointer_t arrp = value_to_ptr(arr); if (init_value != VALUE_UNDEF) for (int i = n + 1; i <= size; i++) @@ -1554,7 +1554,7 @@ static value_t gc_grow_array(value_t obj, int32_t addedElements, int32_t offset) int32_t size = vecp->body[0]; if (new_n > size) { int32_t new_size = real_array_length(new_n); - value_t new_vec = duplicate_fixarray(new_size, offset, vec); + value_t new_vec = duplicate_fixedarray(new_size, offset, vec); objp->body[1] = new_vec; } else @@ -1585,7 +1585,7 @@ value_t gc_make_array(const class_object* clazz, int32_t n, ...) { va_start(args, n); for (int32_t i = 0; i < n; i++) - fast_fixarray_set(arrayp->body[1], i, va_arg(args, value_t)); + fast_fixedarray_set(arrayp->body[1], i, va_arg(args, value_t)); va_end(args); return array; @@ -1600,7 +1600,7 @@ value_t* gc_array_get(value_t obj, int32_t idx) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= idx && idx < len) - return fast_fixarray_get(objp->body[1], idx); + return fast_fixedarray_get(objp->body[1], idx); else { runtime_index_error(idx, len, "Array.get"); return 0; @@ -1611,7 +1611,7 @@ value_t gc_array_set(value_t obj, int32_t index, value_t new_value) { pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; if (0 <= index && index < len) { - fast_fixarray_set(objp->body[1], index, new_value); + fast_fixedarray_set(objp->body[1], index, new_value); return new_value; } else { runtime_index_error(index, len, "Array.set"); @@ -1626,7 +1626,7 @@ int32_t gc_array_push(value_t obj, value_t new_value) { gc_grow_array(obj, 1, 0); pointer_t objp = value_to_ptr(obj); int32_t len = objp->body[0]; - fast_fixarray_set(objp->body[1], len - 1, new_value); + fast_fixedarray_set(objp->body[1], len - 1, new_value); DELETE_ROOT_SET(rootset) return len; } @@ -1637,8 +1637,8 @@ value_t gc_array_pop(value_t obj) { if (len == 0) return VALUE_UNDEF; - value_t value = gc_fixarray_get(objp->body[1], len - 1); - gc_fixarray_set(objp->body[1], len - 1, VALUE_UNDEF); + value_t value = gc_fixedarray_get(objp->body[1], len - 1); + gc_fixedarray_set(objp->body[1], len - 1, VALUE_UNDEF); objp->body[0] = len - 1; return value; } @@ -1649,7 +1649,7 @@ int32_t gc_array_unshift(value_t obj, value_t new_value) { rootset.values[1] = new_value; gc_grow_array(obj, 1, 1); pointer_t objp = value_to_ptr(obj); - fast_fixarray_set(objp->body[1], 0, new_value); + fast_fixedarray_set(objp->body[1], 0, new_value); DELETE_ROOT_SET(rootset) return objp->body[0]; } @@ -1660,7 +1660,7 @@ value_t gc_array_shift(value_t obj) { if (len == 0) return VALUE_UNDEF; - value_t value = gc_fixarray_get(objp->body[1], 0); + value_t value = gc_fixedarray_get(objp->body[1], 0); gc_grow_array(obj, -1, -1); return value; } @@ -1698,7 +1698,7 @@ value_t gc_safe_array_get(value_t obj, int32_t idx) { else if (clazz == &boolarray_object.clazz) return bool_to_value(*gc_bytearray_get(obj, idx)); else if (clazz == &class_FixedArray.clazz) - return gc_fixarray_get(obj, idx); + return gc_fixedarray_get(obj, idx); else if (IS_ARRAY_TYPE(clazz)) // for arrays of value_t return *gc_array_get(obj, idx); else { @@ -1720,7 +1720,7 @@ value_t gc_safe_array_set(value_t obj, int32_t idx, value_t new_value) { return bool_to_value(v); } else if (clazz == &class_FixedArray.clazz) - return gc_fixarray_set(obj, idx, new_value); + return gc_fixedarray_set(obj, idx, new_value); else if (IS_ARRAY_TYPE(clazz)) // for arrays of value_t return gc_array_set(obj, idx, new_value); else { diff --git a/microcontroller/core/test/c-runtime-test.c b/microcontroller/core/test/c-runtime-test.c index d60f93bc..454539f9 100644 --- a/microcontroller/core/test/c-runtime-test.c +++ b/microcontroller/core/test/c-runtime-test.c @@ -111,25 +111,25 @@ static value_t gc_new_array2(int32_t n) { return gc_new_array(NULL, n, VALUE_UNDEF); } -static value_t gc_new_fixarray2(int32_t n) { - return gc_new_fixarray(n, VALUE_UNDEF); +static value_t gc_new_fixedarray2(int32_t n) { + return gc_new_fixedarray(n, VALUE_UNDEF); } static value_t gc_new_array3(int32_t n) { ROOT_SET(root_set, 1) - root_set.values[0] = gc_new_fixarray2(n); - value_t obj = gc_new_fixarray2(1); - gc_fixarray_set(obj, 0, root_set.values[0]); + root_set.values[0] = gc_new_fixedarray2(n); + value_t obj = gc_new_fixedarray2(1); + gc_fixedarray_set(obj, 0, root_set.values[0]); DELETE_ROOT_SET(root_set) return obj; } static value_t gc_array3_get(value_t obj, int32_t index) { - return gc_fixarray_get(gc_fixarray_get(obj, 0), index); + return gc_fixedarray_get(gc_fixedarray_get(obj, 0), index); } static void gc_array3_set(value_t obj, int32_t index, value_t value) { - gc_fixarray_set(gc_fixarray_get(obj, 0), index, value); + gc_fixedarray_set(gc_fixedarray_get(obj, 0), index, value); } // Test functions @@ -233,7 +233,7 @@ void test_String() { check_string_length(s1); check_string_length(s); - check_string_length(gc_new_fixarray(3, VALUE_NULL)); + check_string_length(gc_new_fixedarray(3, VALUE_NULL)); } static value_t gc_bytearray_set(value_t obj, value_t index, value_t new_value) { @@ -258,8 +258,8 @@ void test_bytearray() { Assert_equals(gc_bytearray_length(arr2), 7); } -static value_t gc_fixarray_setter(value_t obj, value_t index, value_t new_value) { - return gc_fixarray_set(obj, value_to_int(index), new_value); +static value_t gc_fixedarray_setter(value_t obj, value_t index, value_t new_value) { + return gc_fixedarray_set(obj, value_to_int(index), new_value); } void test_copy_intarray() { @@ -473,12 +473,12 @@ void test_copy_array() { Assert_equals(*gc_array_get(arr2b, 1), int_to_value(2)); Assert_equals(*gc_array_get(arr2b, 2), int_to_value(3)); - // Test 3: Copy from fixarray (fixed length array) - value_t vec = gc_new_fixarray(3, VALUE_UNDEF); + // Test 3: Copy from fixedarray (fixed length array) + value_t vec = gc_new_fixedarray(3, VALUE_UNDEF); root_set.values[0] = vec; - gc_fixarray_set(vec, 0, int_to_value(5)); - gc_fixarray_set(vec, 1, int_to_value(15)); - gc_fixarray_set(vec, 2, int_to_value(25)); + gc_fixedarray_set(vec, 0, int_to_value(5)); + gc_fixedarray_set(vec, 1, int_to_value(15)); + gc_fixedarray_set(vec, 2, int_to_value(25)); value_t arr3 = gc_copy_array(NULL, vec); root_set.values[1] = arr3; Assert_equals(gc_array_length(arr3), 3); @@ -570,15 +570,15 @@ void test_copy_arrays() { DELETE_ROOT_SET(root_set); } -void test_fixarray() { - value_t arr = gc_new_fixarray2(4); - value_t arr2 = gc_new_fixarray2(4); +void test_fixedarray() { + value_t arr = gc_new_fixedarray2(4); + value_t arr2 = gc_new_fixedarray2(4); for (int i = 0; i < 4; i++) - Assert_equals(gc_fixarray_setter(arr2, int_to_value(i), int_to_value(i)), int_to_value(i)); + Assert_equals(gc_fixedarray_setter(arr2, int_to_value(i), int_to_value(i)), int_to_value(i)); for (int i = 0; i < 4; i++) - gc_fixarray_setter(arr, i, int_to_value(i)); + gc_fixedarray_setter(arr, i, int_to_value(i)); for (int i = 0; i < 4; i++) { - value_t e = gc_fixarray_get(arr2, int_to_value(i)); + value_t e = gc_fixedarray_get(arr2, int_to_value(i)); Assert_equals(value_to_int(e), i); } Assert_equals(value_to_int(gc_array_length(arr)), 4); @@ -606,7 +606,7 @@ void test_allocate_heap() { value_t index = 2; value_t vec_size = heap_size / 1024; for (int i = 0; i < 1024; i++) { - value_t arr = gc_new_fixarray2(vec_size - 2); + value_t arr = gc_new_fixedarray2(vec_size - 2); Assert_pequals(value_to_ptr(arr), &heap_memory[index]); index += vec_size; } @@ -625,7 +625,7 @@ void test_root_set() { root_set.values[0] = gc_new_string("hello"); value_t obj; for (int i = 0; i < 3; i++) - obj = gc_new_fixarray2(4); + obj = gc_new_fixedarray2(4); gc_run(); Assert_equals(heap_memory[0], 4); @@ -645,7 +645,7 @@ void test_root_set2() { root_set.values[0] = gc_new_string("hello"); root_set.values[1] = gc_new_string("hello2"); for (int i = 0; i < 3; i++) - gc_new_fixarray2(3); + gc_new_fixedarray2(3); root_set.values[0] = VALUE_NULL; gc_run(); @@ -660,13 +660,13 @@ void test_root_set2() { void test_nested_root_set2() { ROOT_SET(root_set, 3); - root_set.values[0] = gc_new_fixarray2(1); + root_set.values[0] = gc_new_fixedarray2(1); DELETE_ROOT_SET(root_set); } void test_nested_root_set3() { ROOT_SET(root_set, 3); - root_set.values[0] = gc_new_fixarray2(1); + root_set.values[0] = gc_new_fixedarray2(1); gc_run(); DELETE_ROOT_SET(root_set); } @@ -679,7 +679,7 @@ void test_nested_root_set() { root_set.values[0] = gc_new_string("hello"); root_set.values[1] = gc_new_string("hello2"); for (int i = 0; i < 3; i++) - gc_new_fixarray2(3); + gc_new_fixedarray2(3); test_nested_root_set2(); root_set.values[0] = VALUE_NULL; @@ -776,14 +776,14 @@ void test_gc_liveness2() { ROOT_SET(root_set, 3); value_t obj, obj2, obj3, obj4; - root_set.values[0] = obj = gc_new_fixarray2(4); - obj4 = gc_new_fixarray2(1); - gc_fixarray_setter(obj, int_to_value(0), obj2 = gc_new_bytearray(true, 8, 0)); + root_set.values[0] = obj = gc_new_fixedarray2(4); + obj4 = gc_new_fixedarray2(1); + gc_fixedarray_setter(obj, int_to_value(0), obj2 = gc_new_bytearray(true, 8, 0)); gc_bytearray_set_raw_word(obj2, 0, obj4); - gc_new_fixarray2(1); - gc_new_fixarray2(1); - gc_fixarray_setter(obj, int_to_value(1), obj3 = gc_new_fixarray2(2)); - gc_fixarray_setter(obj3, int_to_value(0), obj); + gc_new_fixedarray2(1); + gc_new_fixedarray2(1); + gc_fixedarray_setter(obj, int_to_value(1), obj3 = gc_new_fixedarray2(2)); + gc_fixedarray_setter(obj3, int_to_value(0), obj); root_set.values[1] = gc_new_string("test"); gc_run(); @@ -808,11 +808,11 @@ void test_gc_sweep() { root_set.values[1] = gc_new_string("test3"); root_set.values[2] = gc_new_string("test4"); gc_new_string("test5"); - gc_new_fixarray2(2); - gc_new_fixarray2(3); - root_set.values[3] = obj = gc_new_fixarray2(4); - obj2 = gc_new_fixarray2(3); - gc_fixarray_setter(obj, 0, obj2); + gc_new_fixedarray2(2); + gc_new_fixedarray2(3); + root_set.values[3] = obj = gc_new_fixedarray2(4); + obj2 = gc_new_fixedarray2(3); + gc_fixedarray_setter(obj, 0, obj2); gc_run(); @@ -869,11 +869,11 @@ void test_gc_write_barrier() { root_set.values[1] = gc_new_string("test3"); root_set.values[2] = gc_new_string("test4"); gc_new_string("test5"); - gc_new_fixarray2(2); - gc_new_fixarray2(3); - root_set.values[3] = obj = gc_new_fixarray2(4); - obj2 = gc_new_fixarray2(3); - gc_fixarray_setter(obj, 0, obj2); + gc_new_fixedarray2(2); + gc_new_fixedarray2(3); + root_set.values[3] = obj = gc_new_fixedarray2(4); + obj2 = gc_new_fixedarray2(3); + gc_fixedarray_setter(obj, 0, obj2); gc_run(); Assert_true(is_live_object(obj1)); diff --git a/microcontroller/core/test/c-runtime-test2.c b/microcontroller/core/test/c-runtime-test2.c index 2fecf3d5..997cc2c8 100644 --- a/microcontroller/core/test/c-runtime-test2.c +++ b/microcontroller/core/test/c-runtime-test2.c @@ -291,12 +291,12 @@ void test_array_push() { value_t arr = gc_new_array(&anyarray_object.clazz, i, int_to_value(i)); value_t arrvec = value_to_ptr(arr)->body[1]; Assert_equals(gc_array_length(arr), i); - Assert_equals(gc_fixarray_length(arrvec), real_len); + Assert_equals(gc_fixedarray_length(arrvec), real_len); Assert_equals(value_to_ptr(arrvec)->body[0], real_len); for (int j = 0; j < i; j++) - Assert_equals(*fast_fixarray_get(arrvec, j), int_to_value(i)); + Assert_equals(*fast_fixedarray_get(arrvec, j), int_to_value(i)); for (int j = i; j < real_len; ++j) - Assert_equals(*fast_fixarray_get(arrvec, j), VALUE_UNDEF); + Assert_equals(*fast_fixedarray_get(arrvec, j), VALUE_UNDEF); if (i > 0) gc_array_set(arr, 0, int_to_value(70 + i)); Assert_equals(value_to_ptr(arr)->body[0], i); @@ -308,14 +308,14 @@ void test_array_push() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; if (i > 0) - Assert_equals(*fast_fixarray_get(vec, 0), int_to_value(70 + i)); + Assert_equals(*fast_fixedarray_get(vec, 0), int_to_value(70 + i)); for (int k = 1; k < i; k++) - Assert_equals(*fast_fixarray_get(vec, k), int_to_value(i)); + Assert_equals(*fast_fixedarray_get(vec, k), int_to_value(i)); Assert_true(len >= i + j + 1); - Assert_equals(*fast_fixarray_get(vec, i + j), int_to_value(90 + j)); + Assert_equals(*fast_fixedarray_get(vec, i + j), int_to_value(90 + j)); for (int k = i + j + 1; k < len; k++) - Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixedarray_get(vec, k), VALUE_UNDEF); } } @@ -334,10 +334,10 @@ void test_array_pop() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; for (int k = 0; k < i - j - 1; k++) - Assert_equals(*fast_fixarray_get(vec, k), int_to_value(100 + k)); + Assert_equals(*fast_fixedarray_get(vec, k), int_to_value(100 + k)); Assert_true(len >= i - j - 1); for (int k = i - j - 1; k < len; k++) - Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixedarray_get(vec, k), VALUE_UNDEF); } Assert_equals(gc_array_length(arr), 0); Assert_equals(gc_array_pop(arr), VALUE_UNDEF); @@ -357,14 +357,14 @@ void test_array_unshift() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; for (int k = 0; k < j + 1; k++) - Assert_equals(*fast_fixarray_get(vec, k), int_to_value(90 + j - k)); + Assert_equals(*fast_fixedarray_get(vec, k), int_to_value(90 + j - k)); for (int k = j + 1; k < i + j + 1; k++) - Assert_equals(*fast_fixarray_get(vec, k), int_to_value(100 + k - j - 1)); + Assert_equals(*fast_fixedarray_get(vec, k), int_to_value(100 + k - j - 1)); Assert_true(len >= i + j + 1); for (int k = i + j + 1; k < len; k++) - Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixedarray_get(vec, k), VALUE_UNDEF); } } } @@ -381,10 +381,10 @@ void test_array_shift() { value_t vec = value_to_ptr(arr)->body[1]; int len = value_to_ptr(vec)->body[0]; for (int k = 0; k < i - j - 1; k++) - Assert_equals(*fast_fixarray_get(vec, k), int_to_value(100 + k + j + 1)); + Assert_equals(*fast_fixedarray_get(vec, k), int_to_value(100 + k + j + 1)); Assert_true(len >= i - j - 1); for (int k = i - j - 1; k < len; k++) - Assert_equals(*fast_fixarray_get(vec, k), VALUE_UNDEF); + Assert_equals(*fast_fixedarray_get(vec, k), VALUE_UNDEF); } Assert_equals(gc_array_length(arr), 0); Assert_equals(gc_array_shift(arr), VALUE_UNDEF); From 0ae77cd4da893c8854bc6eb4de439d29d210d512 Mon Sep 17 00:00:00 2001 From: chibash Date: Sat, 15 Aug 2026 19:21:18 +0900 Subject: [PATCH 6/6] fixes a bug in the compilation of equality expressions for int32 --- .../code-generator/code-generator.ts | 2 +- .../code-generator/code-generator.test.ts | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lang/src/transpiler/code-generator/code-generator.ts b/lang/src/transpiler/code-generator/code-generator.ts index a460b77c..26a6325d 100644 --- a/lang/src/transpiler/code-generator/code-generator.ts +++ b/lang/src/transpiler/code-generator/code-generator.ts @@ -1092,7 +1092,7 @@ export class CodeGenerator extends visitor.NodeVisitor { if (left_type === Int32 && right_type === Any || left_type === Any && right_type === Int32) { this.result.write(`${cr.typeConversion(left_type, Int32, env, left)}`) this.visit(left, env) - this.result.write(`) ${op} `) + this.result.write(`) ${op2} `) this.result.write(`${cr.typeConversion(right_type, Int32, env, right)}`) this.visit(right, env) this.result.write(')') diff --git a/lang/tests/transpiler/code-generator/code-generator.test.ts b/lang/tests/transpiler/code-generator/code-generator.test.ts index 2ef374fc..e25b330e 100644 --- a/lang/tests/transpiler/code-generator/code-generator.test.ts +++ b/lang/tests/transpiler/code-generator/code-generator.test.ts @@ -1135,6 +1135,26 @@ test('int32 computing', () => { ` expect(compileAndRun(src)).toBe('int32\n-2147483648\nint32\n8\n') + + const src2 = ` + function foo(n: int32): int32 { + let i = n + 1 + let j: int32 = 0x7fffff0f + let k: any = 3 + j &= 0xff + if (i > n && i < 100 && n !== k) { + print_i32(-n) + print_i32(~n) + print_i32(n ** 2) + print_i32(j) + } + return n % 3 + i % k + } + + print_i32(foo(5)) + ` + + expect(compileAndRun(src2)).toBe('-5\n-6\n25\n15\n2\n') }) test('int32-type array is compatible with any-type array', () => { @@ -1154,6 +1174,29 @@ test('int32-type array is compatible with any-type array', () => { expect(compileAndRun(src)).toBe('3\n1\n1\n3\n') }) +test('int32 properties', () => { + const src = ` + class A { + i: int32 + j: integer + a: any + constructor() { + this.i = 10 + this.j = 3 + this.a = 200 + } + } + + const a = new A() + a.i += 1 + a.i += a.j + a.i += a.a + print_i32(a.i) + ` + + expect(compileAndRun(src)).toBe('214\n') +}) + test('a subclas contains int32 property', () => { const src = ` class A {