Skip to content

Commit

Permalink
feat: Add nonnull/NonNullable builtin type (#1875)
Browse files Browse the repository at this point in the history
  • Loading branch information
Willem Wyndham committed Jun 8, 2021
1 parent ee5016c commit 42883cb
Show file tree
Hide file tree
Showing 167 changed files with 902 additions and 214 deletions.
1 change: 1 addition & 0 deletions src/common.ts
Expand Up @@ -147,6 +147,7 @@ export namespace CommonNames {
export const indexof = "indexof";
export const valueof = "valueof";
export const returnof = "returnof";
export const nonnull = "nonnull";
// aliases
export const null_ = "null";
export const true_ = "true";
Expand Down
6 changes: 6 additions & 0 deletions src/program.ts
Expand Up @@ -992,6 +992,12 @@ export class Program extends DiagnosticEmitter {
this.makeNativeTypeDeclaration(CommonNames.returnof, CommonFlags.EXPORT | CommonFlags.GENERIC),
DecoratorFlags.BUILTIN
));
this.nativeFile.add(CommonNames.nonnull, new TypeDefinition(
CommonNames.nonnull,
this.nativeFile,
this.makeNativeTypeDeclaration(CommonNames.nonnull, CommonFlags.EXPORT | CommonFlags.GENERIC),
DecoratorFlags.BUILTIN
));

// The following types might not be enabled by compiler options, so the
// compiler needs to check this condition whenever such a value is created
Expand Down
111 changes: 55 additions & 56 deletions src/resolver.ts
Expand Up @@ -296,6 +296,7 @@ export class Resolver extends DiagnosticEmitter {
if (text == CommonNames.indexof) return this.resolveBuiltinIndexofType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.valueof) return this.resolveBuiltinValueofType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.returnof) return this.resolveBuiltinReturnTypeType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.nonnull) return this.resolveBuiltinNotNullableType(node, ctxElement, ctxTypes, reportMode);
}

// Resolve normally
Expand Down Expand Up @@ -438,19 +439,9 @@ export class Resolver extends DiagnosticEmitter {
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
var typeArgumentNodes = node.typeArguments;
if (!typeArgumentNodes || typeArgumentNodes.length != 1) {
if (reportMode == ReportMode.REPORT) {
let numTypeArguments = 0;
if (typeArgumentNodes) numTypeArguments = typeArgumentNodes.length;
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
node.range, "1", numTypeArguments.toString()
);
}
return null;
}
var typeArgument = this.resolveType(typeArgumentNodes[0], ctxElement, ctxTypes, reportMode);
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
var typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
switch (typeArgument.kind) {
case TypeKind.I8:
Expand Down Expand Up @@ -483,26 +474,16 @@ export class Resolver extends DiagnosticEmitter {
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
var typeArgumentNodes = node.typeArguments;
if (!typeArgumentNodes || typeArgumentNodes.length != 1) {
if (reportMode == ReportMode.REPORT) {
let numTypeArguments = 0;
if (typeArgumentNodes) numTypeArguments = typeArgumentNodes.length;
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
node.range, "1", numTypeArguments.toString()
);
}
return null;
}
var typeArgument = this.resolveType(typeArgumentNodes[0], ctxElement, ctxTypes, reportMode);
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
var typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, 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()
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
Expand All @@ -520,7 +501,7 @@ export class Resolver extends DiagnosticEmitter {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNodes[0].range, typeArgument.toString()
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
Expand All @@ -536,19 +517,9 @@ export class Resolver extends DiagnosticEmitter {
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
var typeArgumentNodes = node.typeArguments;
if (!typeArgumentNodes || typeArgumentNodes.length != 1) {
let numTypeArguments = 0;
if (typeArgumentNodes) numTypeArguments = typeArgumentNodes.length;
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
node.range, "1", numTypeArguments.toString()
);
}
return null;
}
var typeArgument = this.resolveType(typeArgumentNodes[0], ctxElement, ctxTypes, reportMode);
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
var typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
var classReference = typeArgument.getClassOrWrapper(this.program);
if (classReference) {
Expand All @@ -558,7 +529,7 @@ export class Resolver extends DiagnosticEmitter {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNodes[0].range, typeArgument.toString()
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
Expand All @@ -574,31 +545,39 @@ export class Resolver extends DiagnosticEmitter {
/** How to proceed with eventualy diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
var typeArgumentNodes = node.typeArguments;
if (!typeArgumentNodes || typeArgumentNodes.length != 1) {
if (reportMode == ReportMode.REPORT) {
let numTypeArguments = 0;
if (typeArgumentNodes) numTypeArguments = typeArgumentNodes.length;
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
node.range, "1", numTypeArguments.toString()
);
}
return null;
}
var typeArgument = this.resolveType(typeArgumentNodes[0], ctxElement, ctxTypes, reportMode);
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
var typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
var signatureReference = typeArgument.getSignature();
if (signatureReference) return signatureReference.returnType;
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Type_0_has_no_call_signatures,
typeArgumentNodes[0].range, typeArgument.toString()
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}

private resolveBuiltinNotNullableType(
/** The type to resolve. */
node: NamedTypeNode,
/** Contextual element. */
ctxElement: Element,
/** Contextual types, i.e. `T`. */
ctxTypes: Map<string,Type> | null = null,
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): Type | null {
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
var typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
if (!typeArgument.isNullableReference) return typeArgument;
return typeArgument.nonNullableType;
}

/** Resolves a type name to the program element it refers to. */
resolveTypeName(
/** The type name to resolve. */
Expand Down Expand Up @@ -3362,4 +3341,24 @@ export class Resolver extends DiagnosticEmitter {
}
return instance;
}

private ensureOneTypeArgument(
/** The type to resolve. */
node: NamedTypeNode,
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.REPORT
): TypeNode | null {
var typeArgumentNodes = node.typeArguments;
let numTypeArguments = 0;
if (!typeArgumentNodes || (numTypeArguments = typeArgumentNodes.length) != 1) {
if (reportMode == ReportMode.REPORT) {
this.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
node.range, "1", numTypeArguments.toString()
);
}
return null;
}
return typeArgumentNodes[0];
}
}
1 change: 1 addition & 0 deletions std/assembly/compat.ts
@@ -1 +1,2 @@
export type ReturnType<T> = returnof<T>;
export type NonNullable<T> = nonnull<T>;
4 changes: 4 additions & 0 deletions std/assembly/index.d.ts
Expand Up @@ -1314,6 +1314,10 @@ declare type valueof<T extends unknown[]> = T[0];
declare type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
/** A special type evaluated to the return type of T if T is a callable function. */
declare type returnof<T extends (...args: any) => any> = ReturnType<T>;
/** A special type that excludes null and undefined from T. */
declare type NonNullable<T> = T extends null | undefined ? never : T;
/** A special type that excludes null and undefined from T. */
declare type nonnull<T> = NonNullable<T>;

/** Pseudo-class representing the backing class of integer types. */
declare class _Integer {
Expand Down
2 changes: 1 addition & 1 deletion tests/asconfig/complicated/asconfig.json
Expand Up @@ -14,4 +14,4 @@
"initialMemory": 100,
"enable": ["simd"]
}
}
}
2 changes: 1 addition & 1 deletion tests/asconfig/extends/expected.json
Expand Up @@ -5,4 +5,4 @@
"noAssert": true,
"enable": ["simd"]
}
}
}
2 changes: 1 addition & 1 deletion tests/asconfig/target/expected.json
Expand Up @@ -3,4 +3,4 @@
"exportRuntime": false,
"noAssert": true
}
}
}

0 comments on commit 42883cb

Please sign in to comment.