Skip to content
Open
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
4 changes: 4 additions & 0 deletions analyser/module_analyser.c2
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,10 @@ fn bool Analyser.analyseGlobalDecl(Analyser* ma, Decl* d) {
case Variable:
ma.analyseGlobalVarDecl((VarDecl*)d);
break;
case When:
// TODO: support this: dispatch to other lists
ma.error(d.getLoc(), "'when' blocks not supported at global scope");
break;
}

d.setChecked();
Expand Down
3 changes: 3 additions & 0 deletions analyser/module_analyser_expr.c2
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ fn IdentifierKind Analyser.setExprFlags(Analyser* ma, Expr** e_ptr, Decl* d) {
break;
}
break;
case When:
break;
}
return kind;
}
Expand Down Expand Up @@ -650,6 +652,7 @@ fn void get_best_match(const char* name1, usize len1, StructTypeDecl* s, usize*
u32 num_members = s.getNumMembers();
for (u32 i = 0; i < num_members; i++) {
Decl* m = members[i];
if (m.isDisabled()) continue;
const char* name2 = m.getName();
if (name2 == nil) {
get_best_match(name1, len1, (StructTypeDecl*)m, best_dist, result);
Expand Down
2 changes: 2 additions & 0 deletions analyser/module_analyser_init.c2
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ fn void fillFieldInfo(StructTypeDecl* std, FillInfo* fi, u32 base_offset) {

for (u32 i=0; i<num_members; i++) {
Decl* d = members[i];
if (d.isDisabled()) continue;
FieldInitField* f = &fi.fii.fields[fi.idx];
f.member_idx = fi.member_idx;
const StructMemberLayout* ml = &layout.members[i];
Expand Down Expand Up @@ -559,6 +560,7 @@ fn bool Analyser.analyseInitListStruct(Analyser* ma, InitListExpr* ile, QualType
const StructLayout* layout = std.getLayout();
u32 member_idx = 0;
for (u32 i=0; i<numValues; i++) {
if (members[i].isDisabled()) continue;
Expr* value = values[i];

const StructMemberLayout* ml = &layout.members[member_idx];
Expand Down
1 change: 1 addition & 0 deletions analyser/module_analyser_member.c2
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ fn ValType decl2valtype(const Decl* d) {
case EnumConstant:
case FunctionType:
case AliasType:
case When:
break;
case Variable:
return LValue;
Expand Down
56 changes: 53 additions & 3 deletions analyser/module_analyser_stmt.c2
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ fn FlowBits Analyser.analyseStmt(Analyser* ma, Stmt** s_ptr, bool checkEffect) {
case Assert:
ma.analyseAssertStmt(s);
return FlowNext;
case When:
flow = ma.analyseWhenStmt(s);
break;
}
if (!(flow & FlowNext)) ma.scope.setUnreachable();
return flow;
Expand Down Expand Up @@ -228,6 +231,14 @@ fn QualType Analyser.analyseCondition(Analyser* ma, Stmt** s_ptr, bool check_ass
return qt;
}

fn bool Analyser.checkWhen(Analyser* ma, Stmt* s) {
if (s.isWhen()) {
ma.error(s.getLoc(), "'when' statement must be braced in this context");
return false;
}
return true;
}

fn FlowBits Analyser.analyseIfStmt(Analyser* ma, Stmt* s) {
FlowBits flow = FlowNext;
IfStmt* i = (IfStmt*)s;
Expand All @@ -237,15 +248,17 @@ fn FlowBits Analyser.analyseIfStmt(Analyser* ma, Stmt* s) {
if (ma.has_error) goto done;
// TODO: handle constant conditions

ma.checkWhen(i.getThen());
ma.scope.enter(scope.Decl);
flow = ma.analyseStmt(i.getThen2(), true);
ma.scope.exit(ma.has_error);

FlowBits flow2 = FlowNext;
Stmt** else_ = i.getElse2();
if (else_) {
Stmt** elsep = i.getElse2();
if (elsep) {
ma.checkWhen(*elsep);
ma.scope.enter(scope.Decl);
flow2 = ma.analyseStmt(else_, true);
flow2 = ma.analyseStmt(elsep, true);
ma.scope.exit(ma.has_error);
}
flow |= flow2;
Expand Down Expand Up @@ -288,6 +301,7 @@ fn FlowBits Analyser.analyseForStmt(Analyser* ma, Stmt* s) {
if (qt.isInvalid()) goto done;
}

ma.checkWhen(f.getBody());
FlowBits flow2 = ma.analyseStmt(f.getBody2(), true);
flow |= flow2 & (FlowReturn | FlowGoto | FlowNoReturn | FlowError);
if (flow2 & FlowBreak) flow |= FlowNext;
Expand Down Expand Up @@ -315,6 +329,7 @@ fn FlowBits Analyser.analyseWhileStmt(Analyser* ma, Stmt* s) {
}

ma.scope.enter(scope.Break | scope.Continue | scope.Decl | scope.Control);
ma.checkWhen(w.getBody());
FlowBits flow2 = ma.analyseStmt(w.getBody2(), true);
ma.scope.exit(ma.has_error);
flow |= flow2 & (FlowReturn | FlowGoto | FlowNoReturn | FlowError);
Expand Down Expand Up @@ -555,3 +570,38 @@ fn void Analyser.checkReturnAddrOfLocal(Analyser* ma, Expr* arg) {
}
}

// when statement modelled after Odin and Nim
fn FlowBits Analyser.analyseWhenStmt(Analyser* ma, Stmt* s) {
WhenStmt* w = (WhenStmt*)s;

QualType qt = ma.analyseConstExpr(w.getCond2(), true);
if (qt.isInvalid()) return FlowError;

Expr* cond = w.getCond();
if (!cond.isCtv()) {
ma.errorRange(cond.getLoc(), cond.getRange(), "'when' condition is not a compile-time value");
return FlowError;
}

Value v = ast.evalExpr(cond);
if (v.kind == Error) {
ma.errorRange(cond.getLoc(), cond.getRange(), "%s", v.error_msg);
return FlowError;
}
if (!v.isZero()) {
w.setTrue();
Stmt* thenStmt = w.getThen();
if (thenStmt.isCompound())
return ma.analyseCompoundStmt((CompoundStmt*)thenStmt);
else
return ma.analyseStmt(w.getThen2(), true);
} else
if (w.hasElse()) {
Stmt* elseStmt = w.getElse();
if (elseStmt.isCompound())
return ma.analyseCompoundStmt((CompoundStmt*)elseStmt);
else
return ma.analyseStmt(w.getElse2(), true);
}
return FlowNext;
}
62 changes: 56 additions & 6 deletions analyser/module_analyser_struct.c2
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ fn void Analyser.analyseStructType(Analyser* ma, StructTypeDecl* d) {
ma.checkStack[ma.checkIndex-1].usedPublic = false;
ma.usedPublic = false;
}
NameMap names.init(d.getNumMembers());

ma.analyseStructNames(d, &names);
// Handle 'when' blocks: disable unselected members
if (d.hasWhen() && !ma.analyseStructTypeWhenBlocks(d)) return;

NameMap names.init(d.getNumMembers());
ma.analyseStructNames(d, &names);
names.free();

ma.analyseStructMembers(d);
Expand All @@ -47,6 +49,7 @@ fn void Analyser.analyseStructMembers(Analyser* ma, StructTypeDecl* d) {

for (u32 i=0; i<count; i++) {
Decl* member = members[i];
if (member.isDisabled()) continue;
if (member.isVariable()) {
VarDecl* vd = (VarDecl*)member;
// Note: dont push to stack, because can self-ref to StructType
Expand Down Expand Up @@ -173,21 +176,19 @@ fn void Analyser.analyseStructMember(Analyser* ma, VarDecl* v) {
}

fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameMap* names) {
// note: already checked that struct doesn't have 0 members
u32 count = d.getNumMembers();
Decl** members = d.getMembers();

for (u32 i=0; i<count; i++) {
// there can more members in anonymous sub-structs
Decl* member = members[i];
if (member.isDisabled()) continue;
u32 name_idx = member.getNameIdx();

StructTypeDecl* sub = nil;
if (member.isStructType()) sub = (StructTypeDecl*)member;

if (name_idx == 0) {
// can be anonymous sub-struct/union or anonymous bit-field
if (member.isStructType()) {
StructTypeDecl* sub = (StructTypeDecl*)member;
ma.analyseStructNames(sub, names);
}
} else {
Expand All @@ -200,6 +201,7 @@ fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameMap* na
names.add(name_idx, member.getLoc());

if (member.isStructType()) {
StructTypeDecl* sub = (StructTypeDecl*)member;
NameMap sub_names.init(sub.getNumMembers());
ma.analyseStructNames(sub, &sub_names);
sub_names.free();
Expand All @@ -208,3 +210,51 @@ fn void Analyser.analyseStructNames(Analyser* ma, StructTypeDecl* d, NameMap* na
}
}

fn bool Analyser.resolveWhenDecl(Analyser* ma, WhenDecl* wd) {
QualType qt = ma.analyseConstExpr(wd.getCond2(), true);
if (qt.isInvalid()) return false;
Expr* cond = wd.getCond();
if (!cond.isCtv()) {
ma.errorRange(cond.getLoc(), cond.getRange(), "'when' condition is not a compile-time value");
return false;
}
Value v = ast.evalExpr(cond);
if (v.kind == Error) {
ma.errorRange(cond.getLoc(), cond.getRange(), "%s", v.error_msg);
return false;
}
if (!v.isZero()) wd.setTrue();
return true;
}

fn bool Analyser.analyseStructTypeWhenBlocks(Analyser* ma, StructTypeDecl* d) {
bool ok = true;
u32 count = d.getNumMembers();
Decl** members = d.getMembers();

for (u32 i = 0; i < count; i++) {
Decl* member = members[i];
if (member.isDisabled()) continue;
if (member.isStructType()) {
StructTypeDecl* sub = (StructTypeDecl*)member;
if (!ma.analyseStructTypeWhenBlocks(sub)) return false;
}
if (member.isWhenDecl()) {
WhenDecl* wd = (WhenDecl*)member;
if (!ma.resolveWhenDecl(wd)) return false;
member.setDisabled();
u32 then_count = wd.getThenCount();
u32 else_count = wd.getElseCount();
u32 from = i + 1;
u32 end1 = from + then_count;
if (wd.isTrue()) {
from += then_count;
end1 += else_count;
}
for (u32 j = from; j < end1; j++) {
members[j].setDisabled();
}
}
}
return ok;
}
2 changes: 2 additions & 0 deletions analyser/size_analyser.c2
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fn void sizeOfUnion(StructTypeDecl* s) {
Decl** members = s.getMembers();
for (u32 i = 0; i < num_members; i++) {
Decl* d = members[i];
if (d.isDisabled()) continue;
TypeSize member = sizeOfType(d.getType());
// TODO handle un-packed sub-structs
u32 size = member.size; // byte size of the current member
Expand Down Expand Up @@ -83,6 +84,7 @@ public fn void sizeOfStruct(StructTypeDecl* s) {

for (u32 i = 0; i < num_members; i++) {
Decl* d = members[i];
if (d.isDisabled()) continue;
// Note: doesn't analyze the bit-field
TypeSize member = sizeOfType(d.getType());
StructMemberLayout* ml = &layout.members[i];
Expand Down
3 changes: 3 additions & 0 deletions analyser/unused_checker.c2
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ fn void Checker.check(void* arg, Decl* d) {
case Variable:
if (c.warnings.no_unused_variable) return;
break;
case When:
return;
}
if (!used && !d.hasAttrUnused() && !d.isExported()) {
c.diags.warn(d.getLoc(), "unused %s '%s'", d.getKindName(), d.getFullName());
Expand Down Expand Up @@ -139,6 +141,7 @@ fn void Checker.checkStructMembers(Checker* c, Decl* d) {
Decl** members = std.getMembers();
for (u32 i=0; i<num_members; i++) {
Decl* member = members[i];
if (member.isDisabled()) continue;
if (member.isStructType()) {
c.checkStructMembers(member);
} else {
Expand Down
7 changes: 7 additions & 0 deletions ast/ast_evaluator.c2
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ fn Cont Stmt.eval(Stmt* s, Evaluator* sf) {
case Decl: return ((DeclStmt*)s).eval(sf);
case Asm: return ((AsmStmt*)s).eval(sf);
case Assert: return ((AssertStmt*)s).eval(sf);
case When: return ((WhenStmt*)s).eval(sf);
}
return Abort;
}
Expand Down Expand Up @@ -751,3 +752,9 @@ fn Cont AssertStmt.eval(AssertStmt* s, Evaluator* sf) {
}
return Normal;
}

fn Cont WhenStmt.eval(WhenStmt* s, Evaluator* sf) {
if (s.isTrue()) return s.getThen().eval(sf);
if (s.hasElse()) return s.getElse().eval(sf);
return Normal;
}
19 changes: 17 additions & 2 deletions ast/decl.c2
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public type DeclKind enum u8 {
FunctionType,
AliasType,
Variable,
When,
}

const char*[DeclKind] declKind_names = {
Expand All @@ -42,6 +43,7 @@ const char*[DeclKind] declKind_names = {
"FunctionType",
"AliasTypeDecl",
"VarDecl",
"WhenDecl",
}

public type DeclCheckState enum u8 {
Expand All @@ -68,9 +70,9 @@ type DeclBits struct {
u32 is_external : 1; // in external lib
u32 is_generated : 1; // for globals, can be used by generators
u32 has_gen_idx : 1;
u32 is_disabled : 1; // potentially merge with check_state
}

const u32 NumDeclBits = 15; // should match bitfields above
const u32 NumDeclBits = 16; // should match bitfields above

public type Decl struct @(opaque) {
union {
Expand All @@ -81,6 +83,7 @@ public type Decl struct @(opaque) {
EnumTypeDeclBits enumTypeDeclBits;
EnumConstantDeclBits enumConstantDeclBits;
VarDeclBits varDeclBits;
WhenDeclBits whenDeclBits;
u32 bits;
}
union {
Expand Down Expand Up @@ -141,6 +144,9 @@ public fn bool Decl.isCheckInProgress(const Decl* d) {
public fn void Decl.setChecked(Decl* d) { d.declBits.check_state = Checked; }
public fn void Decl.setCheckInProgress(Decl* d) { d.declBits.check_state = InProgress; }

public fn void Decl.setDisabled(Decl* d) { d.declBits.is_disabled = true; }
public fn bool Decl.isDisabled(const Decl* d) { return d.declBits.is_disabled; }

public fn void Decl.setHasAttr(Decl* d) { d.declBits.has_attr = 1; }
public fn bool Decl.hasAttr(const Decl* d) { return d.declBits.has_attr; }

Expand Down Expand Up @@ -199,6 +205,10 @@ public fn bool Decl.isVariable(const Decl* d) {
return d.getKind() == Variable;
}

public fn bool Decl.isWhenDecl(const Decl* d) {
return d.getKind() == When;
}

public fn const char* Decl.getName(const Decl* d) {
return idx2name(d.name_idx);
}
Expand Down Expand Up @@ -279,6 +289,7 @@ public fn bool Decl.isTypeDecl(const Decl* d) {
case FunctionType: return true;
case AliasType: return true;
case Variable: break;
case When: break;
}
return false;
}
Expand All @@ -297,6 +308,7 @@ public fn const char* Decl.getKindName(const Decl* d) {
case FunctionType: return "type";
case AliasType: return "type";
case Variable: return "variable";
case When: return "when";
}
return "";
}
Expand Down Expand Up @@ -384,6 +396,9 @@ fn void Decl.print(const Decl* d, string_buffer.Buf* out, u32 indent) {
case Variable:
VarDecl.print((VarDecl*)d, out, indent);
break;
case When:
WhenDecl.print((WhenDecl*)d, out, indent);
break;
}
}

Expand Down
Loading
Loading