Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ export namespace CommonNames {
export const ASC_VERSION_MAJOR = "ASC_VERSION_MAJOR";
export const ASC_VERSION_MINOR = "ASC_VERSION_MINOR";
export const ASC_VERSION_PATCH = "ASC_VERSION_PATCH";
// enums
export const EnumToString = "__enum_to_string";
// classes
export const I8 = "I8";
export const I16 = "I16";
Expand Down
49 changes: 48 additions & 1 deletion src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,43 @@ export class Compiler extends DiagnosticEmitter {
return true;
}

private ensureEnumToString(enumElement: Enum, reportNode: Node): string | null {
if (!this.compileEnum(enumElement)) return null;
if (enumElement.is(CommonFlags.Const)) {
this.errorRelated(
DiagnosticCode.A_const_enum_member_can_only_be_accessed_using_a_string_literal,
reportNode.range, enumElement.identifierNode.range
);
return null;
}
let members = enumElement.members;
if (!members) return null;
if (enumElement.toStringFunctionName) return enumElement.toStringFunctionName;
const functionName = `${enumElement.internalName}#${CommonNames.EnumToString}`;
enumElement.toStringFunctionName = functionName;
const isInline = enumElement.hasDecorator(DecoratorFlags.Inline);
let module = this.module;
let exprs = new Array<ExpressionRef>();
// when the values are the same, TS returns the last enum value name that appears
for (let _keys = Map_keys(members), _values = Map_values(members), i = 1, k = _keys.length; i <= k; ++i) {
let enumValueName = unchecked(_keys[k - i]);
let member = unchecked(_values[k - i]);
if (member.kind != ElementKind.EnumValue) continue;
let enumValue = <EnumValue>member;
const enumValueExpr = isInline
? module.i32(i64_low(enumValue.constantIntegerValue))
: module.global_get(enumValue.internalName, TypeRef.I32);
let expr = module.if(
module.binary(BinaryOp.EqI32, enumValueExpr, module.local_get(0, TypeRef.I32)),
module.return(this.ensureStaticString(enumValueName))
);
exprs.push(expr);
}
exprs.push(module.unreachable());
module.addFunction(functionName, TypeRef.I32, TypeRef.I32, null, module.block(null, exprs, TypeRef.I32));
return functionName;
}

// === Functions ================================================================================

/** Compiles a priorly resolved function. */
Expand Down Expand Up @@ -7092,7 +7129,17 @@ export class Compiler extends DiagnosticEmitter {
): ExpressionRef {
let module = this.module;
let targetExpression = expression.expression;
let targetType = this.resolver.resolveExpression(targetExpression, this.currentFlow); // reports
let resolver = this.resolver;
let targetElement = resolver.lookupExpression(targetExpression, this.currentFlow, Type.auto, ReportMode.Swallow);
if (targetElement && targetElement.kind == ElementKind.Enum) {
const elementExpr = this.compileExpression(expression.elementExpression, Type.i32, Constraints.ConvImplicit);
const toStringFunctionName = this.ensureEnumToString(<Enum>targetElement, expression);
this.currentType = this.program.stringInstance.type;
if (toStringFunctionName == null) return module.unreachable();
return module.call(toStringFunctionName, [ elementExpr ], TypeRef.I32);
}

let targetType = resolver.resolveExpression(targetExpression, this.currentFlow);
if (targetType) {
let classReference = targetType.getClassOrWrapper(this.program);
if (classReference) {
Expand Down
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
"Type '{0}' has no property '{1}'.": 2460,
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
"In 'const' enum declarations member initializer must be constant expression.": 2474,
"A const enum member can only be accessed using a string literal.": 2476,
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
"'{0}' is referenced directly or indirectly in its own base expression.": 2506,
"Cannot create an instance of an abstract class.": 2511,
Expand Down
2 changes: 1 addition & 1 deletion src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3449,7 +3449,7 @@ export class Namespace extends DeclaredElement {

/** An enum. */
export class Enum extends TypedElement {

toStringFunctionName: string | null = null;
/** Constructs a new enum. */
constructor(
/** Simple name. */
Expand Down
7 changes: 7 additions & 0 deletions tests/compiler/enum-to-string-error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"asc_flags": [],
"stderr": [
"TS2476: A const enum member can only be accessed using a string literal.",
"EOF"
]
}
9 changes: 9 additions & 0 deletions tests/compiler/enum-to-string-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const enum CE {
CE0,
CE1,
CE2,
}

assert(CE[CE.CE0] === "CE0");

ERROR("EOF");
Loading
Loading