Skip to content

Commit

Permalink
Merge branch 'release/0.41.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiayang committed Oct 11, 2019
2 parents 37f47d9 + cf227d4 commit 4508c78
Show file tree
Hide file tree
Showing 13 changed files with 248 additions and 120 deletions.
70 changes: 39 additions & 31 deletions build/ultratiny.flx
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,53 @@ class X
var data: int
}

class Y: X
class Y : X
{
init(c: int) : super(c: c + 30) { }
}

class Z: Y
class Z : Y
{
init(c: int) : super(c: c - 60) { }
init(c: int) : super(c: c - 90) { }
}

// issue #2: class inheritance is source-order-dependent (ie. the definition of the base class must
// appear before the definition of any children)

// classes are a Bad Idea (tm) ):

class P
{
var z: int

init(x: int = 3, y: int = 7)
{
this.z = x * y
}

virtual fn foo(x: &Y) => printf("P::foo(%d)\n", x.data)
}

class Q : P
{
init() : super(x: 5, y: 8) { }

// override fn foo(x: int) => printf("Q::foo(%d)\n", x)
}

class R : Q
{
init() : super() { }

override fn foo(x: &X) => printf("R::foo(%d)\n", x.data)
}



class A
{
init() { }
virtual fn lol(a: Y) -> &Y
virtual fn foo(a: &Y) -> &Y
{
std::io::println("A::foo()")
return alloc Y(c: 471)
Expand All @@ -69,36 +101,14 @@ class B : A
{
init() : super() { }

override fn foo(a: Y) -> &Y
override fn foo(a: &X) -> &Z
{
std::io::println("B::foo()")
return alloc Z(c: 748)
// return 3
}
}

// import std::io as _

// @entry fn main()
// {
// println("hello, world!")
// }

// issue #0: we basically don't really even check for overriding methods properly.
// issue #1: co/contra-variance of return and parameter types for virtual methods
// issue #2: crashes when functions have default arguments??

// classes are a Bad Idea (tm) ):

class Tmp
{
var z: int

init(x: int = 3, y: int = 7)
{
this.z = x * y
}
}


@entry fn main()
Expand All @@ -113,10 +123,8 @@ class Tmp

// let q = Tmp(k: 3, m: 7).meth()

// let q = B().foo(Y(c: 1)).data

let q = foo(x: 7)
std::io::println("thing: %", q)
let q = B().foo(alloc Y(c: 1)).data
printf("q = %d\n", q)
}


Expand Down
2 changes: 1 addition & 1 deletion source/codegen/autocasting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ namespace cgn
result = ret;
}
else if(fromType->isPointerType() && target->isPointerType() && fromType->getPointerElementType()->isClassType()
&& fromType->getPointerElementType()->toClassType()->isInParentHierarchy(target->getPointerElementType()))
&& fromType->getPointerElementType()->toClassType()->hasParent(target->getPointerElementType()))
{
auto ret = this->irb.PointerTypeCast(from, target);
result = ret;
Expand Down
3 changes: 2 additions & 1 deletion source/codegen/classes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ CGResult sst::ClassDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer)
// set our vtable
if(clsty->getVirtualMethodCount() > 0)
{
auto vtable = cs->irb.PointerTypeCast(cs->irb.AddressOf(cs->module->getOrCreateVirtualTableForClass(clsty), false), fir::Type::getInt8Ptr());
auto vtable = cs->irb.PointerTypeCast(cs->irb.AddressOf(cs->module->getOrCreateVirtualTableForClass(clsty), false),
fir::Type::getInt8Ptr());
cs->irb.SetVtable(self, vtable);
}

Expand Down
87 changes: 69 additions & 18 deletions source/fir/Types/ClassType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ namespace fir
}


bool ClassType::isInParentHierarchy(Type* base)
bool ClassType::hasParent(Type* base)
{
auto target = dcast(ClassType, base);
if(!target) return false;
Expand Down Expand Up @@ -294,41 +294,92 @@ namespace fir
this->reverseVirtualMethodMap = this->baseClass->reverseVirtualMethodMap;
}

void ClassType::addVirtualMethod(Function* method)

// expects the self param to be removed already!!!
// note: this one doesn't check if the return types are compatible; we expect typechecking to have already
// verified that, and we don't store the return type in the class virtual method map anyway.
static bool _areTypeListsVirtuallyCompatible(const std::vector<Type*>& base, const std::vector<Type*>& fn)
{
//* what this does is compare the arguments without the first parameter,
//* since that's going to be the self parameter, and that's going to be different
auto withoutself = [](std::vector<Type*> p) -> std::vector<Type*> {
p.erase(p.begin());
// parameters must be contravariant, ie. fn must take more general types than base
// return type must be covariant, ie. fn must return a more specific type than base.

return p;
};
// duh
if(base.size() != fn.size())
return false;

auto matching = [&withoutself](const std::vector<Type*>& a, FunctionType* ft) -> bool {
auto bp = withoutself(ft->getArgumentTypes());
// drop the first argument.
for(auto [ base, derv ] : util::zip(base, fn))
{
if(base == derv)
continue;

//* note: we don't call withoutself on 'a' because we expect that to already have been done
//* before it was added.
return Type::areTypeListsEqual(a, bp);
};
if(!derv->isPointerType() || !derv->getPointerElementType()->isClassType()
|| !base->isPointerType() || !base->getPointerElementType()->isClassType())
{
return false;
}

auto bc = base->getPointerElementType()->toClassType();
auto dc = derv->getPointerElementType()->toClassType();

if(!bc->hasParent(dc))
{
debuglogln("%s is not a parent of %s", dc->str(), bc->str());
return false;
}
}

return true;
}

bool ClassType::areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn)
{
bool ret = _areTypeListsVirtuallyCompatible(util::drop(base->getArgumentTypes(), 1), util::drop(fn->getArgumentTypes(), 1));

if(!ret)
return false;

auto baseRet = base->getReturnType();
auto fnRet = fn->getReturnType();

// ok now check the return type.
if(baseRet == fnRet)
return true;

if(baseRet->isPointerType() && baseRet->getPointerElementType()->isClassType()
&& fnRet->isPointerType() && fnRet->getPointerElementType()->isClassType())
{
auto br = baseRet->getPointerElementType()->toClassType();
auto dr = fnRet->getPointerElementType()->toClassType();

return dr->hasParent(br);
}
else
{
return false;
}
}

void ClassType::addVirtualMethod(Function* method)
{
//* note: the 'reverse' virtual method map is to allow us, at translation time, to easily create the vtable without
//* unnecessary searching. When we set a base class, we copy its 'reverse' map; thus, if we don't override anything,
//* our vtable will just refer to the methods in the base class.

//* but if we do override something, we just set the method in our 'reverse' map, which is what we'll use to build
//* the vtable. simple?

auto list = method->getType()->toFunctionType()->getArgumentTypes();
auto list = util::drop(method->getType()->toFunctionType()->getArgumentTypes(), 1);

// check every member of the current mapping -- not the fastest method i admit.
bool found = false;
for(auto vm : this->virtualMethodMap)
{
if(vm.first.first == method->getName().name && matching(vm.first.second, method->getType()->toFunctionType()))
if(vm.first.first == method->getName().name
&& _areTypeListsVirtuallyCompatible(vm.first.second, list))
{
found = true;
this->virtualMethodMap[{ method->getName().name, withoutself(list) }] = vm.second;
this->virtualMethodMap[{ method->getName().name, list }] = vm.second;
this->reverseVirtualMethodMap[vm.second] = method;
break;
}
Expand All @@ -337,7 +388,7 @@ namespace fir
if(!found)
{
// just make a new one.
this->virtualMethodMap[{ method->getName().name, withoutself(list) }] = this->virtualMethodCount;
this->virtualMethodMap[{ method->getName().name, list }] = this->virtualMethodCount;
this->reverseVirtualMethodMap[this->virtualMethodCount] = method;
this->virtualMethodCount++;
}
Expand Down
2 changes: 1 addition & 1 deletion source/fir/Types/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ namespace fir
}
//* note: we don't need to check that 'to' is a class type, because if it's not then the parent check will fail anyway.
else if(from->isPointerType() && to->isPointerType() && from->getPointerElementType()->isClassType()
&& from->getPointerElementType()->toClassType()->isInParentHierarchy(to->getPointerElementType()))
&& from->getPointerElementType()->toClassType()->hasParent(to->getPointerElementType()))
{
// cast from a derived class pointer to a base class pointer
return 2;
Expand Down
20 changes: 11 additions & 9 deletions source/frontend/errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ static std::string getSingleContext(const Location& loc, const std::string& unde



std::string __error_gen_internal(const Location& loc, const std::string& msg, const char* type, bool context)
std::string __error_gen_internal(const Location& loc, const std::string& msg, const char* type, bool context, bool multipart)
{
std::string ret;

Expand Down Expand Up @@ -194,6 +194,7 @@ std::string __error_gen_internal(const Location& loc, const std::string& msg, co
ret += getSingleContext(loc, underlineColour) + "\n";
}

if(!multipart) ret += "\n";
return ret;
}

Expand Down Expand Up @@ -223,10 +224,10 @@ static size_t strprinterrf(const char* fmt, Ts... ts)
return (size_t) fprintf(stderr, "%s", strprintf(fmt, ts...).c_str());
}

template <typename... Ts>
static void outputWithoutContext(const char* type, const Location& loc, const char* fmt, Ts... ts)
// template <typename... Ts>
static void outputWithoutContext(const char* type, const Location& loc, const char* s, bool multi)
{
strprinterrf("%s", __error_gen_internal(loc, strprintf(fmt, ts...), type, false));
strprinterrf("%s", __error_gen_internal(loc, s, type, false, multi));
}


Expand All @@ -237,7 +238,8 @@ static void outputWithoutContext(const char* type, const Location& loc, const ch

void BareError::post()
{
if(!this->msg.empty()) outputWithoutContext(typestr(this->type).c_str(), Location(), this->msg.c_str());
if(!this->msg.empty())
outputWithoutContext(typestr(this->type).c_str(), Location(), this->msg.c_str(), !this->subs.empty());

for(auto other : this->subs)
other->post();
Expand All @@ -248,9 +250,9 @@ void SimpleError::post()
{
if(!this->msg.empty())
{
outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str());
outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str(), !this->subs.empty());
strprinterrf("%s%s%s", this->wordsBeforeContext, this->wordsBeforeContext.size() > 0 ? "\n" : "",
this->printContext ? getSingleContext(this->loc, this->type == MsgType::Note ? COLOUR_BLUE_BOLD : COLOUR_RED_BOLD) + "\n\n" : "");
this->printContext ? getSingleContext(this->loc, this->type == MsgType::Note ? COLOUR_BLUE_BOLD : COLOUR_RED_BOLD) + "\n" : "");
}

for(auto other : this->subs)
Expand All @@ -260,7 +262,7 @@ void SimpleError::post()

void ExampleMsg::post()
{
outputWithoutContext(typestr(this->type).c_str(), Location(), "for example:");
outputWithoutContext(typestr(this->type).c_str(), Location(), "for example:", !this->subs.empty());
strprinterrf("%s\n\n", getSingleContext(Location(), COLOUR_BLUE_BOLD, this->example));

for(auto other : this->subs)
Expand Down Expand Up @@ -470,7 +472,7 @@ void OverloadError::post()

[[noreturn]] void doTheExit(bool trace)
{
fprintf(stderr, "there were errors, compilation cannot continue\n");
fprintf(stderr, "\nthere were errors, compilation cannot continue\n");

if(frontend::getAbortOnError()) abort();
else exit(-1);
Expand Down
8 changes: 7 additions & 1 deletion source/frontend/parser/expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1019,11 +1019,17 @@ namespace parser

if(st.front() == TT::LParen)
{
auto leftloc = st.loc();

st.pop();
ret->args = parseCallArgumentList(st);

if(ret->args.empty())
info(st.loc(), "empty argument list in alloc expression () can be omitted");
{
// parseCallArgumentList consumes the closing )
auto tmp = Location::unionOf(leftloc, st.ploc());
info(tmp, "empty argument list in alloc expression () can be omitted");
}
}


Expand Down
4 changes: 2 additions & 2 deletions source/include/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ namespace frontend
}


std::string __error_gen_internal(const Location& loc, const std::string& msg, const char* type, bool context);
std::string __error_gen_internal(const Location& loc, const std::string& msg, const char* type, bool context, bool multiPart);

template <typename... Ts>
std::string __error_gen(const Location& loc, const char* msg, const char* type, bool, Ts&&... ts)
{
return __error_gen_internal(loc, tinyformat::format(msg, ts...), type, true);
return __error_gen_internal(loc, tinyformat::format(msg, ts...), type, true, false);
}


Expand Down
5 changes: 4 additions & 1 deletion source/include/ir/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ namespace fir
Function* getCopyConstructor();
Function* getMoveConstructor();

bool isInParentHierarchy(Type* base);
bool hasParent(Type* base);

void addVirtualMethod(Function* method);
size_t getVirtualMethodIndex(const std::string& name, FunctionType* ft);
Expand Down Expand Up @@ -818,6 +818,9 @@ namespace fir
static ClassType* createWithoutBody(const Identifier& name);
static ClassType* create(const Identifier& name, const std::vector<std::pair<std::string, Type*>>& members,
const std::vector<Function*>& methods, const std::vector<Function*>& inits);

// returns true if 'fn' is a valid virtual override of 'base'. deals with co/contra-variance
static bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn);
};


Expand Down
Loading

0 comments on commit 4508c78

Please sign in to comment.