Skip to content

Always register non-MVP types and check on actual use #1194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 91 additions & 5 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ import {
CommonNames,
INDEX_SUFFIX,
Feature,
Target
Target,
featureToString
} from "./common";

import {
Expand Down Expand Up @@ -113,6 +114,7 @@ import {
DecoratorKind,
AssertionKind,
SourceKind,
FunctionTypeNode,

Statement,
BlockStatement,
Expand Down Expand Up @@ -961,6 +963,7 @@ export class Compiler extends DiagnosticEmitter {
return false;
}
global.setType(resolvedType);
this.checkTypeSupported(global.type, typeNode);

// Otherwise infer type from initializer
} else if (initializerNode) {
Expand Down Expand Up @@ -1268,6 +1271,9 @@ export class Compiler extends DiagnosticEmitter {
var module = this.module;
var signature = instance.signature;
var bodyNode = instance.prototype.bodyNode;
var declarationNode = instance.declaration;
assert(declarationNode.kind == NodeKind.FUNCTIONDECLARATION || declarationNode.kind == NodeKind.METHODDECLARATION);
this.checkSignatureSupported(instance.signature, (<FunctionDeclaration>declarationNode).signature);

var funcRef: FunctionRef;

Expand Down Expand Up @@ -1343,7 +1349,7 @@ export class Compiler extends DiagnosticEmitter {
// imported function
} else if (instance.is(CommonFlags.AMBIENT)) {
instance.set(CommonFlags.MODULE_IMPORT);
mangleImportName(instance, instance.declaration); // TODO: check for duplicates
mangleImportName(instance, declarationNode); // TODO: check for duplicates
module.addFunctionImport(
instance.internalName,
mangleImportName_moduleName,
Expand Down Expand Up @@ -1576,7 +1582,12 @@ export class Compiler extends DiagnosticEmitter {
);
if (type.isManaged) valueExpr = this.makeRetain(valueExpr);
instance.getterRef = module.addFunction(instance.internalGetterName, nativeThisType, nativeValueType, null, valueExpr);
if (instance.setterRef) instance.set(CommonFlags.COMPILED);
if (instance.setterRef) {
instance.set(CommonFlags.COMPILED);
} else {
let typeNode = instance.typeNode;
if (typeNode) this.checkTypeSupported(instance.type, typeNode);
}
return true;
}

Expand Down Expand Up @@ -1624,7 +1635,12 @@ export class Compiler extends DiagnosticEmitter {
nativeValueType, instance.memoryOffset
)
);
if (instance.getterRef) instance.set(CommonFlags.COMPILED);
if (instance.getterRef) {
instance.set(CommonFlags.COMPILED);
} else {
let typeNode = instance.typeNode;
if (typeNode) this.checkTypeSupported(instance.type, typeNode);
}
return true;
}

Expand Down Expand Up @@ -2856,6 +2872,8 @@ export class Compiler extends DiagnosticEmitter {
makeMap(flow.contextualTypeArguments)
);
if (!type) continue;
this.checkTypeSupported(type, typeNode);

if (initializerNode) {
initExpr = this.compileExpression(initializerNode, type, // reports
Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN
Expand Down Expand Up @@ -6228,6 +6246,12 @@ export class Compiler extends DiagnosticEmitter {
var thisType = (<Class>field.parent).type;
var nativeThisType = thisType.toNativeType();

if (!field.is(CommonFlags.COMPILED)) {
field.set(CommonFlags.COMPILED);
let typeNode = field.typeNode;
if (typeNode) this.checkTypeSupported(field.type, typeNode);
}

if (fieldType.isManaged && thisType.isManaged) {
let tempThis = flow.getTempLocal(thisType, findUsedLocals(valueExpr));
// set before and read after valueExpr executes below ^
Expand Down Expand Up @@ -8818,7 +8842,6 @@ export class Compiler extends DiagnosticEmitter {
for (let i = 0; i < numParameters; ++i) {
operands[i + 1] = module.local_get(i + 1, parameterTypes[i].toNativeType());
}
// TODO: base constructor might be inlined, but makeCallDirect can't do this
stmts.push(
module.local_set(0,
this.makeCallDirect(assert(baseClass.constructorInstance), operands, reportNode, false, true)
Expand Down Expand Up @@ -8927,6 +8950,11 @@ export class Compiler extends DiagnosticEmitter {
);
}
}
if (!fieldInstance.is(CommonFlags.COMPILED)) {
fieldInstance.set(CommonFlags.COMPILED);
let typeNode = fieldInstance.typeNode;
if (typeNode) this.checkTypeSupported(fieldInstance.type, typeNode);
}
this.currentType = fieldType;
return module.load(
fieldType.byteSize,
Expand Down Expand Up @@ -9849,6 +9877,62 @@ export class Compiler extends DiagnosticEmitter {
parentFunction.debugLocations.push(range);
}

/** Checks whether a particular feature is enabled. */
checkFeatureEnabled(feature: Feature, reportNode: Node): bool {
if (!this.options.hasFeature(feature)) {
this.error(
DiagnosticCode.Feature_0_is_not_enabled,
reportNode.range, featureToString(feature)
);
return false;
}
return true;
}

/** Checks whether a particular type is supported. */
checkTypeSupported(type: Type, reportNode: Node): bool {
switch (type.kind) {
case TypeKind.V128: return this.checkFeatureEnabled(Feature.SIMD, reportNode);
case TypeKind.ANYREF: return this.checkFeatureEnabled(Feature.REFERENCE_TYPES, reportNode);
}
if (type.is(TypeFlags.REFERENCE)) {
let classReference = type.classReference;
while (classReference) {
let typeArguments = classReference.typeArguments;
if (typeArguments) {
for (let i = 0, k = typeArguments.length; i < k; ++i) {
if (!this.checkTypeSupported(typeArguments[i], reportNode)) {
return false;
}
}
}
classReference = classReference.base;
}
}
return true;
}

/** Checks whether a particular function signature is supported. */
checkSignatureSupported(signature: Signature, reportNode: FunctionTypeNode): bool {
var supported = true;
var explicitThisType = reportNode.explicitThisType;
if (explicitThisType) {
if (!this.checkTypeSupported(assert(signature.thisType), explicitThisType)) {
supported = false;
}
}
var parameterTypes = signature.parameterTypes;
for (let i = 0, k = parameterTypes.length; i < k; ++i) {
if (!this.checkTypeSupported(parameterTypes[i], reportNode.parameters[i])) {
supported = false;
}
}
if (!this.checkTypeSupported(signature.returnType, reportNode.returnType)) {
supported = false;
}
return supported;
}

// === Specialized code generation ==============================================================

/** Makes a constant zero of the specified type. */
Expand Down Expand Up @@ -10039,6 +10123,8 @@ export class Compiler extends DiagnosticEmitter {
let initializerNode = fieldPrototype.initializerNode;
let parameterIndex = fieldPrototype.parameterIndex;
let initExpr: ExpressionRef;
let typeNode = field.typeNode;
if (typeNode) this.checkTypeSupported(fieldType, typeNode);

// if declared as a constructor parameter, use its value
if (parameterIndex >= 0) {
Expand Down
8 changes: 6 additions & 2 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,8 +725,12 @@ export class Program extends DiagnosticEmitter {
this.makeNativeTypeDeclaration(CommonNames.returnof, CommonFlags.EXPORT | CommonFlags.GENERIC),
DecoratorFlags.BUILTIN
));
if (options.hasFeature(Feature.SIMD)) this.registerNativeType(CommonNames.v128, Type.v128);
if (options.hasFeature(Feature.REFERENCE_TYPES)) this.registerNativeType(CommonNames.anyref, Type.anyref);

// The following types might not be enabled by compiler options, so the
// compiler needs to check this condition whenever such a value is created
// respectively stored or loaded.
this.registerNativeType(CommonNames.v128, Type.v128);
this.registerNativeType(CommonNames.anyref, Type.anyref);

// register compiler hints
this.registerConstantInteger(CommonNames.ASC_TARGET, Type.i32,
Expand Down
32 changes: 32 additions & 0 deletions tests/compiler/features/not-supported.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"asc_flags": [
"--runtime none"
],
"stderr": [
"AS103: Feature 'simd' is not enabled.",
"var a: v128;",
"AS103: Feature 'simd' is not enabled.",
"var b = v128.",
"AS103: Feature 'simd' is not enabled.",
"var c: Array<v128>;",
"AS103: Feature 'simd' is not enabled.",
"var a: v128;",
"AS103: Feature 'simd' is not enabled.",
"var b = v128.",
"AS103: Feature 'simd' is not enabled.",
"let a: v128;",
"AS103: Feature 'simd' is not enabled.",
"let b = v128.",
"AS103: Feature 'simd' is not enabled.",
"a: v128",
"AS103: Feature 'simd' is not enabled.",
"): v128",
"AS103: Feature 'simd' is not enabled.",
"a: v128;",
"AS103: Feature 'simd' is not enabled.",
"b: Array<v128>;",
"AS103: Feature 'simd' is not enabled.",
"get c(): v128",
"EOF"
]
}
35 changes: 35 additions & 0 deletions tests/compiler/features/not-supported.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var a: v128; // type not enabled
var b = v128.splat<i32>(1); // instruction not enabled
var c: Array<v128>; // type not enabled

function test1(): void {
var a: v128; // type not enabled
var b = v128.splat<i32>(1); // instruction not enabled
}
test1();

function test2(): void {
{
let a: v128; // type not enabled
let b = v128.splat<i32>(1); // instruction not enabled
}
}
test2();

function test3(
a: v128 // type not enabled
): v128 { // type not enabled
return unreachable();
}
test3(v128.splat<i32>(1)); // instruction not enabled

class Foo {
a: v128; // type not enabled
b: Array<v128>; // type not enabled
get c(): v128 { // type not enabled
return this.a;
}
}
(new Foo()).c;

ERROR("EOF");