Skip to content

Commit

Permalink
add back call expression + basic control flow analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
marcj committed Jul 28, 2022
1 parent 6da903e commit 007e2cb
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 70 deletions.
9 changes: 8 additions & 1 deletion src/checker/check2.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace ts::vm2 {
case TypeKind::TupleMember: {
if (left->kind != TypeKind::TupleMember) return false;
//todo: handle optional
if (!extends((Type *)left->type, (Type *)right->type)) return false;
if (!extends((Type *) left->type, (Type *) right->type)) return false;

return true;
}
Expand Down Expand Up @@ -168,6 +168,13 @@ namespace ts::vm2 {
}
break;
}
case TypeKind::Parameter: {
switch (left->kind) {
case TypeKind::Parameter:
return extends((Type *) left->type, (Type *) right->type);
}
return extends(left, (Type *) right->type);
}
}
return false;
}
Expand Down
26 changes: 19 additions & 7 deletions src/checker/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -996,15 +996,14 @@ namespace ts::checker {
//which indicates the VM that the function needs to be instantiated first.

auto subroutineIndex = program.pushSubroutineNameLess();

unsigned int size = 0;
for (auto &&param: n->typeParameters->list) {
handle(param, program);
}
program.pushSlots();

for (auto &&param: n->parameters->list) {
handle(param, program);
}
//first comes the return type
size++;
if (n->type) {
handle(n->type, program);
} else {
Expand All @@ -1014,7 +1013,13 @@ namespace ts::checker {
} else {
}
}

for (auto &&param: n->parameters->list) {
size++;
handle(param, program);
}
program.pushOp(OP::Function, node);
program.pushUint16(size);
program.popSubroutine();

program.pushOp(OP::FunctionRef, node);
Expand All @@ -1024,9 +1029,9 @@ namespace ts::checker {
program.pushSubroutine(symbol);
program.pushSlots();

for (auto &&param: n->parameters->list) {
handle(param, program);
}
unsigned int size = 0;
//first comes the return type
size++;
if (n->type) {
handle(n->type, program);
} else {
Expand All @@ -1036,7 +1041,14 @@ namespace ts::checker {
} else {
}
}

for (auto &&param: n->parameters->list) {
size++;
handle(param, program);
}

program.pushOp(OP::Function, node);
program.pushUint16(size);
program.popSubroutine();
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/checker/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,11 @@ namespace ts::checker {
break;
}
case OP::CallExpression: {
params += fmt::format(" &{}", vm::readUint16(bin, i + 1));
params += fmt::format(" {}", vm::readUint16(bin, i + 1));
vm::eatParams(op, &i);
break;
}
case OP::Function:
case OP::Union:
case OP::Tuple:
case OP::TemplateLiteral:
Expand Down
25 changes: 19 additions & 6 deletions src/checker/types2.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace ts::vm2 {
Undefined,
String,
Number,
BigInt,
Boolean,
Literal,
PropertySignature,
Expand All @@ -29,6 +30,9 @@ namespace ts::vm2 {
Tuple,
TupleMember,
TemplateLiteral,
Parameter,
Function,
FunctionRef,
};

//Used in the vm
Expand All @@ -38,11 +42,12 @@ namespace ts::vm2 {
StringLiteral = 1 << 2,
NumberLiteral = 1 << 3,
BooleanLiteral = 1 << 4,
True = 1 << 5,
False = 1 << 6,
Stored = 1 << 6, //Used somewhere as cache or as value (subroutine->result for example), and thus can not be stolen/modified
RestReuse = 1 << 8, //allow to reuse/steal T in ...T
Deleted = 1 << 9, //for debugging purposes
BigIntLiteral = 1 << 5,
True = 1 << 6,
False = 1 << 7,
Stored = 1 << 8, //Used somewhere as cache or as value (subroutine->result for example), and thus can not be stolen/modified
RestReuse = 1 << 9, //allow to reuse/steal T in ...T
Deleted = 1 << 10, //for debugging purposes
};

struct Type;
Expand Down Expand Up @@ -285,6 +290,10 @@ namespace ts::vm2 {
stringifyType((Type *) type->type, r);
break;
}
case TypeKind::Parameter: {
stringifyType((Type *) type->type, r);
break;
}
case TypeKind::Tuple: {
r += "[";
auto current = (TypeRef *) type->type;
Expand All @@ -309,9 +318,9 @@ namespace ts::vm2 {
r += "...";
break;
}
if (current) r += "| ";
stringifyType(current->type, r);
current = current->next;
if (current) r += " | ";
}
break;
}
Expand Down Expand Up @@ -359,6 +368,10 @@ namespace ts::vm2 {
return r;
}

inline bool isOptional(Type *type) {
return type->flag & TypeFlag::Optional;
}

// struct TypeMeta {
// string_view typeName;
// };
Expand Down
1 change: 1 addition & 0 deletions src/checker/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ namespace ts::vm {
*i += 2;
break;
}
case OP::Function:
case OP::Union:
case OP::Tuple:
case OP::TemplateLiteral:
Expand Down
140 changes: 122 additions & 18 deletions src/checker/vm2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ namespace ts::vm2 {
gcQueue[gcQueueIdx++] = type;
}

inline Type * widen(Type * type) {
switch (type->kind) {
case TypeKind::Literal: {
if (type->flag & TypeFlag::StringLiteral) return allocate(TypeKind::String);
if (type->flag & TypeFlag::NumberLiteral) return allocate(TypeKind::Number);
if (type->flag & TypeFlag::BooleanLiteral) return allocate(TypeKind::Boolean);
if (type->flag & TypeFlag::BigIntLiteral) return allocate(TypeKind::BigInt);
}
}

return type;
}

void gc(Type *type) {
//debug("gc refCount={} {} ref={}", type->refCount, stringify(type), (void *) type);
if (type->refCount>0) return;
Expand Down Expand Up @@ -541,7 +554,7 @@ namespace ts::vm2 {
auto frame = frames.at(i);
debug("Frame {} depth={} variables={} initialSp={}", i, frame->depth, frame->variables, frame->initialSp);

if (top > 0) {
if (top>0) {
auto size = top - frame->initialSp;
for (unsigned int j = 0; j<size; j++) {
auto i = top - j - 1;
Expand All @@ -568,7 +581,7 @@ namespace ts::vm2 {
debug("");
}

inline void print(Type *type, char * title = "") {
inline void print(Type *type, char *title = "") {
debug("[{}] {} refCount={} {} ref={}", activeSubroutine->ip, title, type->refCount, stringify(type), (void *) type);
}

Expand Down Expand Up @@ -602,22 +615,113 @@ namespace ts::vm2 {
stack[sp++] = allocate(TypeKind::Null);
break;
}
//case OP::FrameEnd: {
// if (frame->size()>frame->variables) {
// //there is a return value on the stack, which we need to preserve
// auto ret = pop();
// popFrame();
// push(ret);
// } else {
// //throw away the whole stack
// popFrame();
// }
// break;
//}
//case OP::Frame: {
// pushFrame();
// break;
//}
case OP::Unknown: {
stack[sp++] = allocate(TypeKind::Unknown);
break;
}
case OP::Parameter: {
const auto address = activeSubroutine->parseUint32();
auto type = allocate(TypeKind::Parameter);
type->readStorage(bin, address);
type->type = pop();
stack[sp++] = type;
break;
}
case OP::Function: {
const auto size = activeSubroutine->parseUint16();
auto types = frame->pop(size);
auto type = allocate(TypeKind::Function);
//first is always the return type
type->type = useAsRef(types[0]);
if (types.size()>1) {
auto current = (TypeRef *) type->type;
for_each(++types.begin(), types.end(), [&current](auto v) {
current->next = useAsRef(v);
current = current->next;
});
current->next = nullptr;
}
stack[sp++] = type;
break;
}
case OP::FunctionRef: {
const auto address = activeSubroutine->parseUint32();
auto type = allocate(TypeKind::FunctionRef);
type->size = address;
stack[sp++] = type;
break;
}
case OP::Instantiate: {
const auto arguments = activeSubroutine->parseUint16();
auto ref = pop(); //FunctionRef/Class

switch (ref->kind) {
case TypeKind::FunctionRef: {
call(ref->size, arguments);
break;
}
default: {
throw std::runtime_error(fmt::format("Can not instantiate {}", ref->kind));
}
}
break;
}
case OP::CallExpression: {
const auto parameterAmount = activeSubroutine->parseUint16();
auto parameters = frame->pop(parameterAmount);
auto typeToCall = pop();

switch (typeToCall->kind) {
case TypeKind::Function: {
//it's important to handle parameters/typeArguments first before changing the stack with push() since `parameters` is a std::span.
auto current = (TypeRef *) typeToCall->type;
for (unsigned int i = 0;; i++) {
current = (TypeRef *) current->next;
if (!current) break; //end
auto parameter = current->type;
auto optional = isOptional(parameter);
if (i>parameters.size() - 1) {
//parameter not provided
if (!optional && !parameter->type) {
report(fmt::format("An argument for '{}' was not provided.", parameter->text), parameter);
}
break;
}
auto lvalue = parameters[i];
//auto rvalue = reinterpret_pointer_cast<Type>(parameter);
//ExtendableStack stack;
if (!extends(lvalue, parameter)) {
//rerun again with
//report(stack.errorMessage());
report(fmt::format("Argument of type '{}' is not assignable to parameter '{}' of type '{}'", stringify(lvalue), parameter->text, stringify(parameter)));
}
}

//we could convert parameters to a tuple and then run isExtendable() on two tuples,
//necessary for REST parameters.
break;
}
default: {
throw std::runtime_error(fmt::format("CallExpression on {} not handled", typeToCall->kind));
}
}
break;
}
case OP::Widen: {
auto type = pop();
auto widened = widen(type);
push(widened);
if (widened != type) gc(type);
break;
}
case OP::Set: {
const auto address = activeSubroutine->parseUint32();
auto type = pop();
auto subroutine = activeSubroutine->module->getSubroutine(address);
if (subroutine->narrowed) drop(subroutine->narrowed);
subroutine->narrowed = use(type);
break;
}
case OP::Assign: {
auto rvalue = pop();
auto lvalue = pop();
Expand Down
Loading

0 comments on commit 007e2cb

Please sign in to comment.