Skip to content
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
98 changes: 93 additions & 5 deletions tslang/lib/TypeScript/MLIRGenClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1525,18 +1525,98 @@ genContext);

NodeFactory nf(NodeFactoryFlags::None);

// errors in the generated code are reported at the class
auto atClass = [&](auto node) {
node->pos = classDeclarationAST->pos;
node->_end = classDeclarationAST->_end;
return node;
};

NodeArray<ParameterDeclaration> parameters;
NodeArray<Statement> statements;

if (!newClassPtr->baseClasses.empty())
{
auto superExpr = nf.createToken(SyntaxKind::SuperKeyword);
auto callSuper = nf.createCallExpression(superExpr, undefined, undefined);
statements.push_back(nf.createExpressionStatement(callSuper));
// TypeScript's implicit `constructor(...args) { super(...args); }`: it takes the
// parameters of the base constructor, typed as its function is (a generic base's
// already specialized), and passes them on; the base applies its defaults
mlir_ts::FunctionType baseConstructorType;
for (auto baseClass = newClassPtr->baseClasses.front(); baseClass;
baseClass = baseClass->baseClasses.empty() ? ClassInfo::TypePtr() : baseClass->baseClasses.front())
{
if (baseClass->hasConstructor)
{
auto index = baseClass->getMethodIndex(CONSTRUCTOR_NAME);
if (index >= 0)
{
baseConstructorType = baseClass->methods[index].funcType;
}

break;
}
}

NodeArray<Expression> superArguments;
if (baseConstructorType)
{
auto inputs = baseConstructorType.getInputs();
// the first input is `this`
for (auto index = 1u; index < inputs.size(); index++)
{
auto paramType = inputs[index];
auto isRest = baseConstructorType.isVarArg() && index == inputs.size() - 1;
auto isOptional = false;
if (auto optionalType = dyn_cast<mlir_ts::OptionalType>(paramType))
{
paramType = optionalType.getElementType();
isOptional = true;
}

// not a name the field initializers, run in this constructor, could mean
auto paramName = ".arg" + std::to_string(index - 1);

std::string paramTypeAlias;
paramTypeAlias += ".";
paramTypeAlias += newClassPtr->fullName.str();
paramTypeAlias += ".";
paramTypeAlias += CONSTRUCTOR_NAME;
paramTypeAlias += paramName;
getTypeAliasMap()[paramTypeAlias] = { paramType, undefined };

DotDotDotToken dotDotDotToken;
if (isRest)
{
dotDotDotToken = nf.createToken(SyntaxKind::DotDotDotToken);
}

QuestionToken questionToken;
if (isOptional)
{
questionToken = nf.createToken(SyntaxKind::QuestionToken);
}

parameters.push_back(atClass(nf.createParameterDeclaration(
undefined, dotDotDotToken, nf.createIdentifier(stows(paramName)), questionToken,
nf.createTypeReferenceNode(nf.createIdentifier(stows(paramTypeAlias)), undefined))));

Expression argument = atClass(nf.createIdentifier(stows(paramName)));
if (isRest)
{
argument = atClass(nf.createSpreadElement(argument));
}

superArguments.push_back(argument);
}
}

auto superExpr = atClass(nf.createToken(SyntaxKind::SuperKeyword));
auto callSuper = atClass(nf.createCallExpression(superExpr, undefined, superArguments));
statements.push_back(atClass(nf.createExpressionStatement(callSuper)));
}

auto body = nf.createBlock(statements, /*multiLine*/ false);
auto body = atClass(nf.createBlock(statements, /*multiLine*/ false));

auto generatedConstructor = nf.createConstructorDeclaration(undefined, undefined, body);
auto generatedConstructor = atClass(nf.createConstructorDeclaration(undefined, parameters, body));
newClassPtr->extraMembers.push_back(generatedConstructor);
}

Expand Down Expand Up @@ -2806,6 +2886,14 @@ genContext);
propertyDeclaration->initializer);
auto expr_statement = nf.createExpressionStatement(_this_name_equal);

// errors in the assignment (the initializer's cast to the field type) are reported at
// the field, not at the start of the file
for (Node node : {Node(_this), Node(_name), Node(_this_name), Node(_this_name_equal), Node(expr_statement)})
{
node->pos = propertyDeclaration->pos;
node->_end = propertyDeclaration->_end;
}

// NOTE: upward mailbox: drained when the constructor body is generated - see A7
const_cast<GenContext &>(genContext).generatedStatements.push_back(expr_statement.as<Statement>());
}
Expand Down
23 changes: 22 additions & 1 deletion tslang/lib/TypeScript/MLIRGenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,28 @@ namespace mlirgen
{
offsetArgs = 2;
}
if (mlir::failed(mlirGenOperands(callExpression->arguments, operands, funcResult.getType(), genContext, offsetArgs, noReceiverTypesForGenericCall)))

// `super(...)`: the callee is the base class, and the arguments are its constructor's. Without
// the constructor's parameters a spread had no count to fill and passed nothing: `super(a, ...r)`
// gave the base an empty rest parameter.
auto operandsFuncType = funcResult.getType();
if (auto classStorageType = dyn_cast<mlir_ts::ClassStorageType>(funcType))
{
for (auto classInfo = getClassInfoByFullName(classStorageType.getName().getValue()); classInfo;
classInfo = classInfo->baseClasses.empty() ? ClassInfo::TypePtr() : classInfo->baseClasses.front())
{
auto index = classInfo->getMethodIndex(CONSTRUCTOR_NAME);
if (index >= 0)
{
operandsFuncType = classInfo->methods[index].funcType;
// after `this`
offsetArgs = 1;
break;
}
}
}

if (mlir::failed(mlirGenOperands(callExpression->arguments, operands, operandsFuncType, genContext, offsetArgs, noReceiverTypesForGenericCall)))
{
return mlir::failure();
}
Expand Down
20 changes: 20 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ tslang_add_test(NAME test-compile-00-class-accessor-2 COMMAND test-runner "${PRO
tslang_add_test(NAME test-compile-00-class-accessor-super COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_super.ts")
tslang_add_test(NAME test-compile-00-class-accessor-virtual COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_virtual.ts")
tslang_add_test(NAME test-compile-00-class-super COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super.ts")
tslang_add_test(NAME test-compile-00-class-implicit-constructor COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_implicit_constructor.ts")
tslang_add_test(NAME test-compile-00-class-super-static COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super_static.ts")
tslang_add_test(NAME test-compile-00-class-default-constructor COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_def_constr.ts")
tslang_add_test(NAME test-compile-00-class-constructor-fields COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_constr_fields.ts")
Expand Down Expand Up @@ -1055,6 +1056,7 @@ tslang_add_test(NAME test-jit-00-class-accessor-2 COMMAND test-runner -jit "${PR
tslang_add_test(NAME test-jit-00-class-accessor-super COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_super.ts")
tslang_add_test(NAME test-jit-00-class-accessor-virtual COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_accessor_virtual.ts")
tslang_add_test(NAME test-jit-00-class-super COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super.ts")
tslang_add_test(NAME test-jit-00-class-implicit-constructor COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_implicit_constructor.ts")
tslang_add_test(NAME test-jit-00-class-super-static COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_super_static.ts")
tslang_add_test(NAME test-jit-00-class-default-constructor COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_def_constr.ts")
tslang_add_test(NAME test-jit-00-class-static-constructor COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_static_constr.ts")
Expand Down Expand Up @@ -1837,6 +1839,7 @@ set(TSLANG_CORPUS
00class_structural_extends.ts
00class_super_static.ts
00class_super.ts
00class_implicit_constructor.ts
00class_symbol_iterator.ts
00class_var_args.ts
00class_virtual_call.ts
Expand Down Expand Up @@ -2845,6 +2848,23 @@ set_tests_properties(test-compile-diagnostic-location-generated-cast
PROPERTIES PASS_REGULAR_EXPRESSION "generated_cast\\.ts:6:23: error: can't cast from 'null' to 'string'"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|generated_cast\\.ts:1:")

# An error in a field initializer, or in a derived class's implicit constructor, is reported at the
# field or the class - not at 1:1, where the code the compiler generates for them had put it.
add_test(NAME test-compile-diagnostic-location-field-initializer
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/field_initializer.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-field-initializer.obj")
set_tests_properties(test-compile-diagnostic-location-field-initializer
PROPERTIES PASS_REGULAR_EXPRESSION "field_initializer\\.ts:6:5: error: can't cast from 'null' to 'Reference<string>'"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|field_initializer\\.ts:1:")
add_test(NAME test-compile-diagnostic-location-implicit-constructor
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/implicit_constructor.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-implicit-constructor.obj")
set_tests_properties(test-compile-diagnostic-location-implicit-constructor
PROPERTIES PASS_REGULAR_EXPRESSION "implicit_constructor\\.ts:9:1: error: Class constructor is not accessable"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|implicit_constructor\\.ts:1:")

# A call to the collector's API (GC_gcollect, or the weak-link functions DefaultLib's WeakRef uses)
# is an error under every model but gc, which alone links the collector - it used to reach the
# linker as an unresolved symbol. Under gc the same file compiles.
Expand Down
7 changes: 7 additions & 0 deletions tslang/test/tester/diagnostic-location/field_initializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// An error in a field's initializer was reported at the start of the file: the assignment the
// compiler generates for it in the constructor had no position.

class Args {
count = 1;
protected argv: Ref<string> = null;
}
11 changes: 11 additions & 0 deletions tslang/test/tester/diagnostic-location/implicit_constructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// An error in a derived class's implicit constructor was reported at the start of the file.
// Here the base's constructor is private, so the implicit one can't call it.

class Base {
private constructor(protected a: int) {
}
}

class Derived extends Base {
extra = 1;
}
103 changes: 103 additions & 0 deletions tslang/test/tester/tests/00class_implicit_constructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// regression test: a derived class without a constructor gets TypeScript's implicit one,
// `constructor(...args) { super(...args); }`.
//
// bugs found and fixed:
// - a derived class with field initializers (and no constructor) was given a generated
// `constructor() { super(); }`, so `new Derived(2, 3)` failed with "Expected 1-2 arguments,
// but got 0", reported at line 1. It now takes the base constructor's parameters (optional
// ones and a rest parameter included) and passes them on.
// - `super(a, ...rest)` passed an empty rest parameter: the arguments of a super call were
// generated without the constructor's parameters, so a spread had nothing to fill.

class Base {
constructor(protected a: int, protected b: int = 10, public c?: string, ...rest: int[]) {
this.restLength = rest.length;
}

restLength: int;

sum(): int {
return this.a + this.b;
}
}

class Derived extends Base {
extra = 2;
}

// the implicit constructor of a class whose base has one too
class MoreDerived extends Derived {
more = 3;
}

class SpreadSuper extends Base {
constructor(a: int, ...rest: int[]) {
super(a, 1, "s", ...rest);
}
}

class Generic<T> {
constructor(public v: T) {
}
}

class FromSpecialized extends Generic<string> {
w = 5;
}

class FromGeneric<T> extends Generic<T> {
w = 6;
}

class NoConstructorBase {
x = 1;
}

class NoConstructorDerived extends NoConstructorBase {
y = 2;
}

// a field initializer runs in the implicit constructor, and still sees the outer `arg0`
const arg0 = 99;
class Shadow extends Base {
z = arg0;
}

function main() {
const d = new Derived(2, 3, "s", 7, 8);
assert(d.sum() == 5);
assert(d.extra == 2);
assert(d.c == "s");
assert(d.restLength == 2);

// the base's default and optional parameters
const d2 = new Derived(4);
assert(d2.sum() == 14);
assert(d2.c == undefined);
assert(d2.restLength == 0);

const m = new MoreDerived(1, 2, "t", 9);
assert(m.sum() == 3);
assert(m.extra == 2);
assert(m.more == 3);
assert(m.restLength == 1);

const s = new SpreadSuper(1, 7, 8, 9);
assert(s.restLength == 3);

const f = new FromSpecialized("hello");
assert(f.v == "hello");
assert(f.w == 5);

const g = new FromGeneric<number>(1.5);
assert(g.v == 1.5);
assert(g.w == 6);

const n = new NoConstructorDerived();
assert(n.x == 1);
assert(n.y == 2);

assert(new Shadow(1).z == 99);

print("done.");
}
Loading