diff --git a/tslang/lib/TypeScript/LowerToLLVM.cpp b/tslang/lib/TypeScript/LowerToLLVM.cpp index a01c87f55..43f4f4f25 100644 --- a/tslang/lib/TypeScript/LowerToLLVM.cpp +++ b/tslang/lib/TypeScript/LowerToLLVM.cpp @@ -4418,13 +4418,31 @@ struct GlobalOpLowering : public TsLlvmPattern { createAsGlobalConstructor = true; } + else if (auto binaryOp = dyn_cast(op); + binaryOp && isa(binaryOp.getResult().getType())) + { + // `"text" + 4` is a string concatenation, which allocates, even when every operand + // is a constant (#469) + createAsGlobalConstructor = true; + } else if (auto castOp = dyn_cast(op)) { auto castType = castOp.getRes().getType(); + auto inType = castOp.getIn().getType(); + if (auto literalType = dyn_cast(inType)) + { + inType = literalType.getElementType(); + } + if (isa(castType) || isa(castType)) { createAsGlobalConstructor = true; } + else if (isa(castType) && !isa(inType)) + { + // a number or a boolean made into a string allocates it (#469) + createAsGlobalConstructor = true; + } else { // see CreateUnionInstanceOp above: a cast to or from a tagged union lowers to diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 0f53f727a..bf29b37f8 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -467,6 +467,7 @@ tslang_add_test(NAME test-compile-00-void COMMAND test-runner "${PROJECT_SOURCE_ tslang_add_test(NAME test-compile-00-in COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00in.ts") tslang_add_test(NAME test-compile-00-in-method-names COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00in_method_names.ts") tslang_add_test(NAME test-compile-00-class-structural-extends COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_structural_extends.ts") +tslang_add_test(NAME test-compile-00-global-constant-concat COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_constant_concat.ts") tslang_add_test(NAME test-compile-00-global-object-literal COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_object_literal.ts") tslang_add_test(NAME test-compile-00-instanceof COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00instanceof.ts") tslang_add_test(NAME test-compile-00-const-of-let COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_of_let.ts") @@ -955,6 +956,7 @@ tslang_add_test(NAME test-jit-00-void COMMAND test-runner -jit "${PROJECT_SOURCE tslang_add_test(NAME test-jit-00-in COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00in.ts") tslang_add_test(NAME test-jit-00-in-method-names COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00in_method_names.ts") tslang_add_test(NAME test-jit-00-class-structural-extends COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_structural_extends.ts") +tslang_add_test(NAME test-jit-00-global-constant-concat COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_constant_concat.ts") tslang_add_test(NAME test-jit-00-global-object-literal COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_object_literal.ts") tslang_add_test(NAME test-jit-00-instanceof COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00instanceof.ts") tslang_add_test(NAME test-jit-00-const-of-let COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_of_let.ts") @@ -1777,6 +1779,7 @@ set(TSLANG_CORPUS 00infer.ts 00global_union_from_call.ts 00instanceof.ts + 00global_constant_concat.ts 00const_of_let.ts 00instanceof_namespace_narrowing.ts 00instanceof_union.ts diff --git a/tslang/test/tester/tests/00global_constant_concat.ts b/tslang/test/tester/tests/00global_constant_concat.ts new file mode 100644 index 000000000..b750862a4 --- /dev/null +++ b/tslang/test/tester/tests/00global_constant_concat.ts @@ -0,0 +1,22 @@ +// A module-level variable initialised with a string concatenation of constants, read by a function +// (#469). The initializer looked constant, so it was placed where nothing may run, and the +// concatenation (and the number's conversion to a string) failed to compile there: "ops with side +// effects not allowed in global initializers". +let S = "text" + 4; +const C = "text" + "four"; +let U: string | null = "nullable" + 5; +let N = "" + 6.5; +let P = "plain"; + +function main() { + assert(S == "text4", "a let from a constant concatenation"); + assert(C == "textfour", "a const from two string constants"); + assert(U == "nullable5", "a nullable string from a constant concatenation"); + assert(N == "6.5", "a number converted to a string"); + assert(P == "plain", "a plain string constant stays as it is"); + + S = S + "!"; + assert(S == "text4!"); + + print("done."); +}