Skip to content

Commit

Permalink
Merge branch 'feature/transparent-fields' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiayang committed Apr 28, 2019
2 parents 45e3d2b + 08779e7 commit a389e1c
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 82 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,16 @@ do {

### Building the Flax compiler

#### Dependencies ####
- LLVM 7, mostly due to their obsession with changing the IR interface every damn version (6 does not work, 8 does not work)
- GMP/MPIR
- MPFR


#### macOS / Linux

- Flax uses a makefile; most likely some form of GNU-compatible `make` will work.
- LLVM needs to be installed. On macOS, `brew install llvm@6` should work. (note: llvm 7 and up seems to have changed some JIT-related things)
- LLVM needs to be installed. On macOS, `brew install llvm@7` should work. (note: llvm 8 and up seems to have changed some JIT-related things)
- For macOS people, simply call `make`.
- Linux people, call `make linux`.
- A *C++17*-compatible compiler should be used.
Expand Down
33 changes: 18 additions & 15 deletions build/supertiny.flx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,29 @@

export supertiny

// import libc as _
import libc
import libc as _

// import std::io
// import std::map
import std::opt

@raw union ipv4
{
_: struct {
_: @raw union {
bytes2: [u8: 4]
raw3: u32
}
}

_: struct {
bytes: [u8: 4]
}

_: struct {
raw2: u32
}

raw: u32
}

Expand All @@ -29,19 +39,12 @@ struct foo

@entry fn main()
{
libc::printf("hello world %d\n", 1)
// do {
// var addr: ipv4
// // addr.raw = -16668480;
// addr.raw = 0xff01a8c0;

// printf("%d.%d.%d.%d\n", addr._.bytes[0], addr._.bytes[1], addr._.bytes[2], addr._.bytes[3]);

// // addr._.bytes[0] = 192
// // addr._.bytes[1] = 168
// // addr._.bytes[2] = 1
// // addr._.bytes[3] = 1
// }
do {
var addr: ipv4
addr.raw3 = 0xff01a8c0;

printf("%d.%d.%d.%d\n", addr.bytes[0], addr.bytes[1], addr.bytes[2], addr.bytes[3]);
}


/* do {
Expand Down
54 changes: 37 additions & 17 deletions source/frontend/parser/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ namespace parser
StructDefn* defn = util::pool<StructDefn>(st.loc());
if(nameless)
{
defn->name = strprintf("__anon_struct_%zu", anon_counter);
defn->name = strprintf("__anon_struct_%zu", anon_counter++);
}
else
{
Expand Down Expand Up @@ -174,20 +174,28 @@ namespace parser
auto loc = st.loc();
std::string name = st.eat().str();

if(auto it = std::find_if(defn->fields.begin(), defn->fields.end(), [&name](const auto& p) -> bool {
return std::get<0>(p) == name;
}); it != defn->fields.end())
// we can't check for duplicates when it's transparent, duh
// we'll collapse and collect and check during typechecking.
if(name != "_")
{
SimpleError::make(loc, "duplicate field '%s' in struct definition", name)
->append(SimpleError::make(MsgType::Note, std::get<1>(*it), "field '%s' previously defined here:", name))
->postAndQuit();
if(auto it = std::find_if(defn->fields.begin(), defn->fields.end(), [&name](const auto& p) -> bool {
return std::get<0>(p) == name;
}); it != defn->fields.end())
{
SimpleError::make(loc, "duplicate field '%s' in struct definition", name)
->append(SimpleError::make(MsgType::Note, std::get<1>(*it), "field '%s' previously defined here:", name))
->postAndQuit();
}
}

if(st.eat() != TT::Colon)
error(st.ploc(), "expected type specifier after field name in struct");

pts::Type* type = parseType(st);
defn->fields.push_back(std::make_tuple(name, loc, type));

if(st.front() == TT::Equal)
error(st.loc(), "struct fields cannot have initialisers");
}
else if(st.front() == TT::Func)
{
Expand Down Expand Up @@ -235,7 +243,7 @@ namespace parser
UnionDefn* defn = util::pool<UnionDefn>(st.loc());
if(nameless)
{
defn->name = strprintf("__anon_union_%zu", anon_counter);
defn->name = strprintf("__anon_union_%zu", anon_counter++);
}
else
{
Expand Down Expand Up @@ -289,13 +297,7 @@ namespace parser
pts::Type* type = 0;
std::string name = st.eat().str();

if(auto it = defn->cases.find(name); it != defn->cases.end())
{
SimpleError::make(loc, "duplicate variant '%s' in union definition", name)
->append(SimpleError::make(MsgType::Note, std::get<1>(it->second), "variant '%s' previously defined here:", name))
->postAndQuit();
}

// to improve code flow, handle the type first.
if(st.front() == TT::Colon)
{
st.eat();
Expand All @@ -310,8 +312,26 @@ namespace parser
error(st.loc(), "expected newline after union variant");
}

if(type == nullptr) type = pts::NamedType::create(loc, VOID_TYPE_STRING);
defn->cases[name] = { index, loc, type };
if(name == "_")
{
if(!israw)
error(loc, "transparent fields can only be present in raw unions");

iceAssert(type);
defn->transparentFields.push_back({ loc, type });
}
else
{
if(auto it = defn->cases.find(name); it != defn->cases.end())
{
SimpleError::make(loc, "duplicate variant '%s' in union definition", name)
->append(SimpleError::make(MsgType::Note, std::get<1>(it->second), "variant '%s' previously defined here:", name))
->postAndQuit();
}

if(type == nullptr) type = pts::NamedType::create(loc, VOID_TYPE_STRING);
defn->cases[name] = { index, loc, type };
}

// do some things
if(st.front() == TT::NewLine || st.front() == TT::Semicolon)
Expand Down
2 changes: 2 additions & 0 deletions source/include/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ namespace ast

bool israw = false;
util::hash_map<std::string, std::tuple<size_t, Location, pts::Type*>> cases;

std::vector<std::pair<Location, pts::Type*>> transparentFields;
};

struct TypeExpr : Expr
Expand Down
1 change: 1 addition & 0 deletions source/include/sst.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ namespace sst
virtual CGResult _codegen(cgn::CodegenState* cs, fir::Type* inferred = 0) override;

util::hash_map<std::string, StructFieldDefn*> fields;
std::vector<StructFieldDefn*> transparentFields;
};


Expand Down
116 changes: 82 additions & 34 deletions source/typecheck/dotop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,34 @@ static ErrorMsg* wrongDotOpError(ErrorMsg* e, sst::StructDefn* str, const Locati



struct search_result_t
{
search_result_t() { }
search_result_t(fir::Type* t, size_t i, bool tr) : type(t), fieldIdx(i), isTransparent(tr) { }

fir::Type* type = 0;
size_t fieldIdx = 0;
bool isTransparent = false;
};


static sst::FieldDotOp* resolveFieldNameDotOp(sst::TypecheckState* fs, sst::TypeDefn* defn, sst::Expr* lhs,
static std::vector<search_result_t> searchTransparentFields(sst::TypecheckState* fs, std::vector<search_result_t> stack,
const std::vector<sst::StructFieldDefn*>& fields, const Location& loc, const std::string& name)
{
size_t idx = 0;
// search for them by name first, instead of doing a super-depth-first-search.
for(auto df : fields)
{
if(df->id.name == name)
{
auto ret = util::pool<sst::FieldDotOp>(loc, df->type);
ret->lhs = lhs;
ret->rhsIdent = name;

return ret;
stack.push_back(search_result_t(df->type, 0, false));
return stack;
}
else if(df->isTransparentField)
}


size_t idx = 0;
for(auto df : fields)
{
if(df->isTransparentField)
{
auto ty = df->type;
assert(ty->isRawUnionType() || ty->isStructType());
Expand All @@ -92,47 +102,85 @@ static sst::FieldDotOp* resolveFieldNameDotOp(sst::TypecheckState* fs, sst::Type
flds = str->fields;

else if(auto unn = dcast(sst::RawUnionDefn, defn); unn)
flds = util::map(util::pairs(unn->fields), [](const auto& x) -> auto { return x.second; });
flds = util::map(util::pairs(unn->fields), [](const auto& x) -> auto { return x.second; }) + unn->transparentFields;

else
error(loc, "what kind of type is this? '%s'", ty);

stack.push_back(search_result_t(ty, idx, true));
auto ret = searchTransparentFields(fs, stack, flds, loc, name);

// hmm.
auto hmm = sst::FieldDotOp(loc, df->type);
if(!ret.empty()) return ret;
else stack.pop_back();
}

hmm.lhs = lhs;
hmm.rhsIdent = "";
hmm.isTransparentField = true;
hmm.indexOfTransparentField = idx;
idx += 1;
}

auto hmm2 = resolveFieldNameDotOp(fs, defn, &hmm, flds, loc, name);
if(hmm2)
{
// make 'hmm' into a pool node.
iceAssert(hmm2->lhs == &hmm);
// if we've reached the end of the line, return nothing.
return { };
}

auto real = util::pool<sst::FieldDotOp>(hmm.loc, hmm.type);
real->lhs = lhs;
real->rhsIdent = "";
real->isTransparentField = true;
real->indexOfTransparentField = idx;

hmm2->lhs = real;
static sst::FieldDotOp* resolveFieldNameDotOp(sst::TypecheckState* fs, sst::Expr* lhs, const std::vector<sst::StructFieldDefn*>& fields,
const Location& loc, const std::string& name)
{
for(auto df : fields)
{
if(df->id.name == name)
{
auto ret = util::pool<sst::FieldDotOp>(loc, df->type);
ret->lhs = lhs;
ret->rhsIdent = name;

return hmm2;
}
return ret;
}
}

idx += 1;
// sad. search for the field, recursively, in transparent members.
auto ops = searchTransparentFields(fs, { }, fields, loc, name);
if(ops.empty())
return nullptr;

// ok, now we just need to make a link of fielddotops...
sst::Expr* cur = lhs;
for(const auto& x : ops)
{
auto op = util::pool<sst::FieldDotOp>(loc, x.type);

op->lhs = cur;
op->isTransparentField = x.isTransparent;
op->indexOfTransparentField = x.fieldIdx;

// don't set a name if we're transparent.
op->rhsIdent = (x.isTransparent ? "" : name);

cur = op;
}

return nullptr;
auto ret = dcast(sst::FieldDotOp, cur);
assert(ret);

return ret;
}


















static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* dotop, fir::Type* infer)
{
auto lhs = dotop->left->typecheck(fs).expr();
Expand Down Expand Up @@ -532,7 +580,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d

while(copy)
{
auto hmm = resolveFieldNameDotOp(fs, copy, lhs, copy->fields, dotop->loc, name);
auto hmm = resolveFieldNameDotOp(fs, lhs, copy->fields, dotop->loc, name);
if(hmm) return hmm;

// ok, we didn't find it.
Expand Down Expand Up @@ -622,8 +670,8 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d
{
if(auto fld = dcast(ast::Ident, dotop->right))
{
auto flds = util::map(util::pairs(rnn->fields), [](const auto& x) -> auto { return x.second; });
auto hmm = resolveFieldNameDotOp(fs, rnn, lhs, flds, dotop->loc, fld->name);
auto flds = util::map(util::pairs(rnn->fields), [](const auto& x) -> auto { return x.second; }) + rnn->transparentFields;
auto hmm = resolveFieldNameDotOp(fs, lhs, flds, dotop->loc, fld->name);
if(hmm)
{
return hmm;
Expand Down
Loading

0 comments on commit a389e1c

Please sign in to comment.