Skip to content

Commit 4508c78

Browse files
committed
Merge branch 'release/0.41.5'
2 parents 37f47d9 + cf227d4 commit 4508c78

File tree

13 files changed

+248
-120
lines changed

13 files changed

+248
-120
lines changed

build/ultratiny.flx

+39-31
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,53 @@ class X
4444
var data: int
4545
}
4646

47-
class Y: X
47+
class Y : X
4848
{
4949
init(c: int) : super(c: c + 30) { }
5050
}
5151

52-
class Z: Y
52+
class Z : Y
5353
{
54-
init(c: int) : super(c: c - 60) { }
54+
init(c: int) : super(c: c - 90) { }
5555
}
5656

57+
// issue #2: class inheritance is source-order-dependent (ie. the definition of the base class must
58+
// appear before the definition of any children)
59+
60+
// classes are a Bad Idea (tm) ):
61+
62+
class P
63+
{
64+
var z: int
65+
66+
init(x: int = 3, y: int = 7)
67+
{
68+
this.z = x * y
69+
}
70+
71+
virtual fn foo(x: &Y) => printf("P::foo(%d)\n", x.data)
72+
}
73+
74+
class Q : P
75+
{
76+
init() : super(x: 5, y: 8) { }
77+
78+
// override fn foo(x: int) => printf("Q::foo(%d)\n", x)
79+
}
80+
81+
class R : Q
82+
{
83+
init() : super() { }
84+
85+
override fn foo(x: &X) => printf("R::foo(%d)\n", x.data)
86+
}
87+
88+
5789

5890
class A
5991
{
6092
init() { }
61-
virtual fn lol(a: Y) -> &Y
93+
virtual fn foo(a: &Y) -> &Y
6294
{
6395
std::io::println("A::foo()")
6496
return alloc Y(c: 471)
@@ -69,36 +101,14 @@ class B : A
69101
{
70102
init() : super() { }
71103

72-
override fn foo(a: Y) -> &Y
104+
override fn foo(a: &X) -> &Z
73105
{
74106
std::io::println("B::foo()")
75107
return alloc Z(c: 748)
76-
// return 3
77108
}
78109
}
79110

80-
// import std::io as _
81-
82-
// @entry fn main()
83-
// {
84-
// println("hello, world!")
85-
// }
86111

87-
// issue #0: we basically don't really even check for overriding methods properly.
88-
// issue #1: co/contra-variance of return and parameter types for virtual methods
89-
// issue #2: crashes when functions have default arguments??
90-
91-
// classes are a Bad Idea (tm) ):
92-
93-
class Tmp
94-
{
95-
var z: int
96-
97-
init(x: int = 3, y: int = 7)
98-
{
99-
this.z = x * y
100-
}
101-
}
102112

103113

104114
@entry fn main()
@@ -113,10 +123,8 @@ class Tmp
113123

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

116-
// let q = B().foo(Y(c: 1)).data
117-
118-
let q = foo(x: 7)
119-
std::io::println("thing: %", q)
126+
let q = B().foo(alloc Y(c: 1)).data
127+
printf("q = %d\n", q)
120128
}
121129

122130

source/codegen/autocasting.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ namespace cgn
176176
result = ret;
177177
}
178178
else if(fromType->isPointerType() && target->isPointerType() && fromType->getPointerElementType()->isClassType()
179-
&& fromType->getPointerElementType()->toClassType()->isInParentHierarchy(target->getPointerElementType()))
179+
&& fromType->getPointerElementType()->toClassType()->hasParent(target->getPointerElementType()))
180180
{
181181
auto ret = this->irb.PointerTypeCast(from, target);
182182
result = ret;

source/codegen/classes.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ CGResult sst::ClassDefn::_codegen(cgn::CodegenState* cs, fir::Type* infer)
8787
// set our vtable
8888
if(clsty->getVirtualMethodCount() > 0)
8989
{
90-
auto vtable = cs->irb.PointerTypeCast(cs->irb.AddressOf(cs->module->getOrCreateVirtualTableForClass(clsty), false), fir::Type::getInt8Ptr());
90+
auto vtable = cs->irb.PointerTypeCast(cs->irb.AddressOf(cs->module->getOrCreateVirtualTableForClass(clsty), false),
91+
fir::Type::getInt8Ptr());
9192
cs->irb.SetVtable(self, vtable);
9293
}
9394

source/fir/Types/ClassType.cpp

+69-18
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ namespace fir
240240
}
241241

242242

243-
bool ClassType::isInParentHierarchy(Type* base)
243+
bool ClassType::hasParent(Type* base)
244244
{
245245
auto target = dcast(ClassType, base);
246246
if(!target) return false;
@@ -294,41 +294,92 @@ namespace fir
294294
this->reverseVirtualMethodMap = this->baseClass->reverseVirtualMethodMap;
295295
}
296296

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

304-
return p;
305-
};
306+
// duh
307+
if(base.size() != fn.size())
308+
return false;
306309

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

310-
//* note: we don't call withoutself on 'a' because we expect that to already have been done
311-
//* before it was added.
312-
return Type::areTypeListsEqual(a, bp);
313-
};
316+
if(!derv->isPointerType() || !derv->getPointerElementType()->isClassType()
317+
|| !base->isPointerType() || !base->getPointerElementType()->isClassType())
318+
{
319+
return false;
320+
}
321+
322+
auto bc = base->getPointerElementType()->toClassType();
323+
auto dc = derv->getPointerElementType()->toClassType();
324+
325+
if(!bc->hasParent(dc))
326+
{
327+
debuglogln("%s is not a parent of %s", dc->str(), bc->str());
328+
return false;
329+
}
330+
}
331+
332+
return true;
333+
}
314334

335+
bool ClassType::areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn)
336+
{
337+
bool ret = _areTypeListsVirtuallyCompatible(util::drop(base->getArgumentTypes(), 1), util::drop(fn->getArgumentTypes(), 1));
338+
339+
if(!ret)
340+
return false;
341+
342+
auto baseRet = base->getReturnType();
343+
auto fnRet = fn->getReturnType();
344+
345+
// ok now check the return type.
346+
if(baseRet == fnRet)
347+
return true;
348+
349+
if(baseRet->isPointerType() && baseRet->getPointerElementType()->isClassType()
350+
&& fnRet->isPointerType() && fnRet->getPointerElementType()->isClassType())
351+
{
352+
auto br = baseRet->getPointerElementType()->toClassType();
353+
auto dr = fnRet->getPointerElementType()->toClassType();
354+
355+
return dr->hasParent(br);
356+
}
357+
else
358+
{
359+
return false;
360+
}
361+
}
362+
363+
void ClassType::addVirtualMethod(Function* method)
364+
{
315365
//* note: the 'reverse' virtual method map is to allow us, at translation time, to easily create the vtable without
316366
//* unnecessary searching. When we set a base class, we copy its 'reverse' map; thus, if we don't override anything,
317367
//* our vtable will just refer to the methods in the base class.
318368

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

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

324374
// check every member of the current mapping -- not the fastest method i admit.
325375
bool found = false;
326376
for(auto vm : this->virtualMethodMap)
327377
{
328-
if(vm.first.first == method->getName().name && matching(vm.first.second, method->getType()->toFunctionType()))
378+
if(vm.first.first == method->getName().name
379+
&& _areTypeListsVirtuallyCompatible(vm.first.second, list))
329380
{
330381
found = true;
331-
this->virtualMethodMap[{ method->getName().name, withoutself(list) }] = vm.second;
382+
this->virtualMethodMap[{ method->getName().name, list }] = vm.second;
332383
this->reverseVirtualMethodMap[vm.second] = method;
333384
break;
334385
}
@@ -337,7 +388,7 @@ namespace fir
337388
if(!found)
338389
{
339390
// just make a new one.
340-
this->virtualMethodMap[{ method->getName().name, withoutself(list) }] = this->virtualMethodCount;
391+
this->virtualMethodMap[{ method->getName().name, list }] = this->virtualMethodCount;
341392
this->reverseVirtualMethodMap[this->virtualMethodCount] = method;
342393
this->virtualMethodCount++;
343394
}

source/fir/Types/Type.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ namespace fir
127127
}
128128
//* 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.
129129
else if(from->isPointerType() && to->isPointerType() && from->getPointerElementType()->isClassType()
130-
&& from->getPointerElementType()->toClassType()->isInParentHierarchy(to->getPointerElementType()))
130+
&& from->getPointerElementType()->toClassType()->hasParent(to->getPointerElementType()))
131131
{
132132
// cast from a derived class pointer to a base class pointer
133133
return 2;

source/frontend/errors.cpp

+11-9
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ static std::string getSingleContext(const Location& loc, const std::string& unde
155155

156156

157157

158-
std::string __error_gen_internal(const Location& loc, const std::string& msg, const char* type, bool context)
158+
std::string __error_gen_internal(const Location& loc, const std::string& msg, const char* type, bool context, bool multipart)
159159
{
160160
std::string ret;
161161

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

197+
if(!multipart) ret += "\n";
197198
return ret;
198199
}
199200

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

226-
template <typename... Ts>
227-
static void outputWithoutContext(const char* type, const Location& loc, const char* fmt, Ts... ts)
227+
// template <typename... Ts>
228+
static void outputWithoutContext(const char* type, const Location& loc, const char* s, bool multi)
228229
{
229-
strprinterrf("%s", __error_gen_internal(loc, strprintf(fmt, ts...), type, false));
230+
strprinterrf("%s", __error_gen_internal(loc, s, type, false, multi));
230231
}
231232

232233

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

238239
void BareError::post()
239240
{
240-
if(!this->msg.empty()) outputWithoutContext(typestr(this->type).c_str(), Location(), this->msg.c_str());
241+
if(!this->msg.empty())
242+
outputWithoutContext(typestr(this->type).c_str(), Location(), this->msg.c_str(), !this->subs.empty());
241243

242244
for(auto other : this->subs)
243245
other->post();
@@ -248,9 +250,9 @@ void SimpleError::post()
248250
{
249251
if(!this->msg.empty())
250252
{
251-
outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str());
253+
outputWithoutContext(typestr(this->type).c_str(), this->loc, this->msg.c_str(), !this->subs.empty());
252254
strprinterrf("%s%s%s", this->wordsBeforeContext, this->wordsBeforeContext.size() > 0 ? "\n" : "",
253-
this->printContext ? getSingleContext(this->loc, this->type == MsgType::Note ? COLOUR_BLUE_BOLD : COLOUR_RED_BOLD) + "\n\n" : "");
255+
this->printContext ? getSingleContext(this->loc, this->type == MsgType::Note ? COLOUR_BLUE_BOLD : COLOUR_RED_BOLD) + "\n" : "");
254256
}
255257

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

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

266268
for(auto other : this->subs)
@@ -470,7 +472,7 @@ void OverloadError::post()
470472

471473
[[noreturn]] void doTheExit(bool trace)
472474
{
473-
fprintf(stderr, "there were errors, compilation cannot continue\n");
475+
fprintf(stderr, "\nthere were errors, compilation cannot continue\n");
474476

475477
if(frontend::getAbortOnError()) abort();
476478
else exit(-1);

source/frontend/parser/expr.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -1019,11 +1019,17 @@ namespace parser
10191019

10201020
if(st.front() == TT::LParen)
10211021
{
1022+
auto leftloc = st.loc();
1023+
10221024
st.pop();
10231025
ret->args = parseCallArgumentList(st);
10241026

10251027
if(ret->args.empty())
1026-
info(st.loc(), "empty argument list in alloc expression () can be omitted");
1028+
{
1029+
// parseCallArgumentList consumes the closing )
1030+
auto tmp = Location::unionOf(leftloc, st.ploc());
1031+
info(tmp, "empty argument list in alloc expression () can be omitted");
1032+
}
10271033
}
10281034

10291035

source/include/errors.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ namespace frontend
8686
}
8787

8888

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

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

9797

source/include/ir/type.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ namespace fir
762762
Function* getCopyConstructor();
763763
Function* getMoveConstructor();
764764

765-
bool isInParentHierarchy(Type* base);
765+
bool hasParent(Type* base);
766766

767767
void addVirtualMethod(Function* method);
768768
size_t getVirtualMethodIndex(const std::string& name, FunctionType* ft);
@@ -818,6 +818,9 @@ namespace fir
818818
static ClassType* createWithoutBody(const Identifier& name);
819819
static ClassType* create(const Identifier& name, const std::vector<std::pair<std::string, Type*>>& members,
820820
const std::vector<Function*>& methods, const std::vector<Function*>& inits);
821+
822+
// returns true if 'fn' is a valid virtual override of 'base'. deals with co/contra-variance
823+
static bool areMethodsVirtuallyCompatible(FunctionType* base, FunctionType* fn);
821824
};
822825

823826

0 commit comments

Comments
 (0)