Add built-in valueof type

This commit is contained in:
dcode 2019-06-15 22:44:48 +02:00
parent a4e5857f7f
commit 8571df939f
10 changed files with 333 additions and 39 deletions

View File

@ -134,6 +134,7 @@ export namespace CommonSymbols {
export const boolean = "boolean";
export const string = "string";
export const native = "native";
export const valueof = "valueof";
// aliases
export const null_ = "null";
export const true_ = "true";

View File

@ -567,6 +567,12 @@ export class Program extends DiagnosticEmitter {
this.makeNativeTypeDeclaration(CommonSymbols.native, CommonFlags.EXPORT | CommonFlags.GENERIC),
DecoratorFlags.BUILTIN
));
this.nativeFile.add(CommonSymbols.valueof, new TypeDefinition(
CommonSymbols.valueof,
this.nativeFile,
this.makeNativeTypeDeclaration(CommonSymbols.valueof, CommonFlags.EXPORT | CommonFlags.GENERIC),
DecoratorFlags.BUILTIN
));
if (options.hasFeature(Feature.SIMD)) this.registerNativeType(CommonSymbols.v128, Type.v128);
// register compiler hints

View File

@ -212,10 +212,10 @@ export class Resolver extends DiagnosticEmitter {
var typeNode = <TypeNode>node;
var typeName = typeNode.name;
var typeArgumentNodes = typeNode.typeArguments;
var possiblyPlaceholder = !typeName.next;
var isSimpleType = !typeName.next;
// look up in contextual type arguments if possibly a placeholder
if (possiblyPlaceholder) {
// look up in contextual type arguments if a simple type
if (isSimpleType) {
if (contextualTypeArguments && contextualTypeArguments.has(typeName.identifier.text)) {
let type = contextualTypeArguments.get(typeName.identifier.text)!;
if (typeArgumentNodes !== null && typeArgumentNodes.length) {
@ -314,41 +314,11 @@ export class Resolver extends DiagnosticEmitter {
return type;
}
// handle special native type
if (possiblyPlaceholder && typeName.identifier.text == CommonSymbols.native) {
if (!(typeArgumentNodes && typeArgumentNodes.length == 1)) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
typeNode.range, "1", (typeArgumentNodes ? typeArgumentNodes.length : 1).toString(10)
);
}
return null;
}
let typeArgument = this.resolveType(
typeArgumentNodes[0],
context,
contextualTypeArguments,
reportMode
);
if (!typeArgument) return null;
switch (typeArgument.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32: return Type.i32;
case TypeKind.ISIZE: if (!this.program.options.isWasm64) return Type.i32;
case TypeKind.I64: return Type.i64;
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.BOOL: return Type.u32;
case TypeKind.USIZE: if (!this.program.options.isWasm64) return Type.u32;
case TypeKind.U64: return Type.u64;
case TypeKind.F32: return Type.f32;
case TypeKind.F64: return Type.f64;
case TypeKind.V128: return Type.v128;
case TypeKind.VOID: return Type.void;
default: assert(false);
// handle built-in types
if (isSimpleType) {
switch (typeName.identifier.symbol) {
case CommonSymbols.native: return this.resolveBuiltinNativeType(typeNode, context, contextualTypeArguments, reportMode);
case CommonSymbols.valueof: return this.resolveBuiltinValueofType(typeNode, context, contextualTypeArguments, reportMode)
}
}
@ -402,6 +372,110 @@ export class Resolver extends DiagnosticEmitter {
return null;
}
private resolveBuiltinNativeType(
/** The type to resolve. */
typeNode: TypeNode,
/** Relative context. */
context: Element,
/** Type arguments inherited through context, i.e. `T`. */
contextualTypeArguments: Map<string,Type> | null = null,
/** How to proceed with eventualy diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
var typeArgumentNodes = typeNode.typeArguments;
if (!(typeArgumentNodes && typeArgumentNodes.length == 1)) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
typeNode.range, "1", (typeArgumentNodes ? typeArgumentNodes.length : 1).toString(10)
);
}
return null;
}
var typeArgument = this.resolveType(typeArgumentNodes[0], context, contextualTypeArguments, reportMode);
if (!typeArgument) return null;
switch (typeArgument.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32: return Type.i32;
case TypeKind.ISIZE: if (!this.program.options.isWasm64) return Type.i32;
case TypeKind.I64: return Type.i64;
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.BOOL: return Type.u32;
case TypeKind.USIZE: if (!this.program.options.isWasm64) return Type.u32;
case TypeKind.U64: return Type.u64;
case TypeKind.F32: return Type.f32;
case TypeKind.F64: return Type.f64;
case TypeKind.V128: return Type.v128;
case TypeKind.VOID: return Type.void;
default: assert(false);
}
return null;
}
private resolveBuiltinValueofType(
/** The type to resolve. */
typeNode: TypeNode,
/** Relative context. */
context: Element,
/** Type arguments inherited through context, i.e. `T`. */
contextualTypeArguments: Map<string,Type> | null = null,
/** How to proceed with eventualy diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
var typeArgumentNodes = typeNode.typeArguments;
if (!(typeArgumentNodes && typeArgumentNodes.length == 1)) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
typeNode.range, "1", (typeArgumentNodes ? typeArgumentNodes.length : 1).toString(10)
);
}
return null;
}
var typeArgument = this.resolveType(typeArgumentNodes[0], context, contextualTypeArguments, reportMode);
if (!typeArgument) return null;
var classReference = typeArgument.classReference;
if (!classReference) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNodes[0].range, typeArgument.toString()
);
}
return null;
}
var program = this.program;
var mapPrototype = program.mapPrototype;
var setPrototype = program.setPrototype;
var arrayPrototype = program.arrayPrototype;
if (classReference.extends(arrayPrototype)) {
let actualTypeArguments = assert(classReference.getTypeArgumentsTo(arrayPrototype));
assert(actualTypeArguments.length == 1);
return actualTypeArguments[0];
} else if (classReference.extends(mapPrototype)) {
let actualTypeArguments = assert(classReference.getTypeArgumentsTo(arrayPrototype));
assert(actualTypeArguments.length == 2);
return actualTypeArguments[1];
} else if (classReference.extends(setPrototype)) {
let actualTypeArguments = assert(classReference.getTypeArgumentsTo(arrayPrototype));
assert(actualTypeArguments.length == 1);
return actualTypeArguments[0];
} else {
let overload = classReference.lookupOverload(OperatorKind.INDEXED_GET);
if (overload) return overload.signature.returnType;
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNodes[0].range, typeArgument.toString()
);
}
return null;
}
}
/** Resolves a type name to the program element it refers to. */
resolveTypeName(
/** The type name to resolve. */

View File

@ -882,6 +882,8 @@ declare namespace v8x16 {
}
/** Macro type evaluating to the underlying native WebAssembly type. */
declare type native<T> = T;
/** Special type evaluating the value type of a collection. */
declare type valueof<T extends unknown[]> = T[0];
/** Pseudo-class representing the backing class of integer types. */
declare class _Integer {

View File

@ -1068,7 +1068,7 @@ function FOREACH<TArray extends ArrayBufferView, T>(
// @ts-ignore: decorator
@inline
export function REVERSE<TArray extends ArrayBufferView, T>(array: TArray): TArray {
function REVERSE<TArray extends ArrayBufferView, T>(array: TArray): TArray {
var dataStart = array.dataStart;
for (let front = 0, back = array.length - 1; front < back; ++front, --back) {
let frontPtr = dataStart + (<usize>front << alignof<T>());

View File

@ -28,6 +28,9 @@ declare type usize = number;
declare type f32 = number;
declare type f64 = number;
/** Special type evaluating the value type of a collection. */
declare type valueof<T extends unknown[]> = T[0];
// Compiler hints
/** Compiler target. 0 = JS, 1 = WASM32, 2 = WASM64. */

View File

@ -0,0 +1,5 @@
{
"asc_flags": [
"--runtime none"
]
}

View File

@ -0,0 +1,9 @@
(module
(type $FUNCSIG$v (func))
(memory $0 1)
(data (i32.const 8) "\14\00\00\00\01\00\00\00\01\00\00\00\14\00\00\00v\00a\00l\00u\00e\00o\00f\00.\00t\00s")
(export "memory" (memory $0))
(func $start (; 0 ;) (type $FUNCSIG$v)
nop
)
)

26
tests/compiler/valueof.ts Normal file
View File

@ -0,0 +1,26 @@
// simple
assert(isInteger<valueof<i8[]>>());
assert(isSigned<valueof<i8[]>>());
assert(sizeof<valueof<i8[]>>() == 1);
// alias
type u32Array = u32[];
assert(isInteger<valueof<u32Array>>());
assert(!isSigned<valueof<u32Array>>());
assert(sizeof<valueof<u32Array>>() == 4);
// float
assert(isFloat<valueof<f32[]>>());
assert(sizeof<valueof<f32[]>>() == 4);
// string
assert(isString<valueof<string[]>>());
assert(isManaged<valueof<string[]>>());
// array
assert(isArray<valueof<string[][]>>());
// typed array
assert(isInteger<valueof<Uint8ClampedArray>>());
assert(!isSigned<valueof<Uint8ClampedArray>>());
assert(sizeof<valueof<Uint8ClampedArray>>() == 1);

View File

@ -0,0 +1,168 @@
(module
(type $FUNCSIG$viiii (func (param i32 i32 i32 i32)))
(type $FUNCSIG$v (func))
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
(memory $0 1)
(data (i32.const 8) "\14\00\00\00\01\00\00\00\01\00\00\00\14\00\00\00v\00a\00l\00u\00e\00o\00f\00.\00t\00s\00")
(table $0 1 funcref)
(elem (i32.const 0) $null)
(export "memory" (memory $0))
(start $start)
(func $start:valueof (; 1 ;) (type $FUNCSIG$v)
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 2
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 3
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.const 1
i32.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 4
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 8
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 0
i32.eqz
i32.eqz
if
i32.const 0
i32.const 24
i32.const 9
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 4
i32.const 4
i32.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 10
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 13
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 4
i32.const 4
i32.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 14
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 17
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 18
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 21
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 24
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 0
i32.eqz
i32.eqz
if
i32.const 0
i32.const 24
i32.const 25
i32.const 0
call $~lib/builtins/abort
unreachable
end
i32.const 1
i32.const 1
i32.eq
i32.eqz
if
i32.const 0
i32.const 24
i32.const 26
i32.const 0
call $~lib/builtins/abort
unreachable
end
)
(func $start (; 2 ;) (type $FUNCSIG$v)
call $start:valueof
)
(func $null (; 3 ;) (type $FUNCSIG$v)
)
)