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 ast/decl.c2
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ public fn u32 Decl.getNameIdx(const Decl* d) {
return d.name_idx;
}

public fn void Decl.setNameIdx(Decl* d, u32 name_idx) {
d.name_idx = name_idx;
}

// convenience function
public fn const char* Decl.getModuleName(const Decl* d) {
const AST* a = d.getAST();
Expand Down
3 changes: 3 additions & 0 deletions ast/function_decl.c2
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static_assert(elemsof(CallKind), elemsof(callKind_names));
type FunctionDeclBits struct {
u32 : NumDeclBits;
u32 is_variadic : 1;
u32 is_local : 1;
u32 has_prefix : 1; // is a (static) type-function
u32 call_kind : 2; // CallKind
u32 has_return : 1; // if it returns something, set during analysis
Expand Down Expand Up @@ -90,6 +91,7 @@ public fn FunctionDecl* FunctionDecl.create(ast_context.Context* c,
VarDecl** params,
u32 num_params,
bool is_variadic,
bool is_local,
bool is_type)
{
u32 size = sizeof(FunctionDecl) + num_params * sizeof(VarDecl*) + rtype.getExtraSize();
Expand All @@ -100,6 +102,7 @@ public fn FunctionDecl* FunctionDecl.create(ast_context.Context* c,
d.base.functionDeclBits.is_variadic = is_variadic;
d.base.functionDeclBits.call_kind = prefix ? CallKind.StaticTypeFunc : CallKind.Normal;
d.base.functionDeclBits.is_type = is_type;
d.base.functionDeclBits.is_local = is_local;
d.body = nil;
d.rt = QualType_Invalid;
d.num_params = (u8)num_params;
Expand Down
22 changes: 14 additions & 8 deletions common/ast_builder.c2
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ public fn Decl* Builder.actOnFunctionTypeDecl(Builder* b,
params,
num_params,
is_variadic,
true);
false, // is_local
true); // is_type

FunctionTypeDecl* d = FunctionTypeDecl.create(b.context, fd);
b.ast.addTypeDecl(d.asDecl());
Expand All @@ -209,7 +210,8 @@ public fn Decl* Builder.actOnStructMemberType(Builder* b,
params,
num_params,
is_variadic,
false);
false, // is_local
false); // is_type
fd.setMemberType();
return (Decl*)fd;
}
Expand Down Expand Up @@ -729,9 +731,10 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
const Ref* prefix,
VarDecl** params,
u32 num_params,
bool is_variadic)
bool is_variadic,
bool is_local)
{
is_public |= b.is_interface;
is_public |= b.is_interface & !is_local;
FunctionDecl* f = FunctionDecl.create(b.context,
name,
loc,
Expand All @@ -742,10 +745,13 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
params,
num_params,
is_variadic,
false);
is_local,
false); // is_type
b.ast.addFunc(f);
if (!prefix) b.addSymbol(name, f.asDecl());
if (b.is_interface) f.asDecl().setExternal();
if (!is_local) {
if (!prefix) b.addSymbol(name, f.asDecl());
if (b.is_interface) f.asDecl().setExternal();
}
return f;
}

Expand Down Expand Up @@ -1074,7 +1080,7 @@ public fn void Builder.insertImplicitCast(Builder* b,
*e_ptr = ic;
}

fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
public fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
Decl* old = b.mod.findSymbol(name_idx);
if (old) {
b.diags.error(d.getLoc(), "redefinition of '%s'", idx2name(name_idx));
Expand Down
105 changes: 81 additions & 24 deletions parser/c2_parser.c2
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ fn void Parser.parseTopLevel(Parser* p) {
p.error("assert can only be used inside a function");
break;
case KW_fn:
p.parseFuncDecl(is_public);
p.parseFuncDecl(is_public, false);
break;
case KW_import:
p.error("no imports allowed after declarations");
Expand Down Expand Up @@ -453,43 +453,97 @@ fn void Parser.parseParamOptionalAttributes(Parser* p, VarDecl* d) {
p.expectAndConsume(Kind.RParen);
}

fn void Parser.parseFuncDecl(Parser* p, bool is_public) {
fn u32 Parser.createLocalName(Parser* p, u32 name, SrcLoc loc) {
char[128] buf;
snprintf(buf, sizeof(buf), "%s__%d", p.pool.idx2str(name), loc);
return p.pool.addStr(buf, true);
}

fn Expr* Parser.parseLocalFuncExpr(Parser* p) {
SrcLoc loc = p.tok.loc;
FunctionDecl* f = p.parseFuncDecl(false, true);
Decl* d = f.asDecl();
u32 name = d.getNameIdx();
u32 local_name = p.createLocalName(name, loc);
d.setNameIdx(local_name);
p.builder.addSymbol(local_name, d);
Expr* fexpr = p.builder.actOnIdentifier(loc, local_name).asExpr();
return fexpr;
}

fn Stmt* Parser.parseLocalFuncStmt(Parser* p) {
SrcLoc loc = p.tok.loc;
FunctionDecl* f = p.parseFuncDecl(false, true);
Decl* d = f.asDecl();
u32 name = d.getNameIdx();
u32 local_name = p.createLocalName(name, loc);
d.setNameIdx(local_name);
p.builder.addSymbol(local_name, d);
Expr* fexpr = p.builder.actOnIdentifier(loc, local_name).asExpr();
// TODO: use auto typing
TypeRefHolder ref.init();
ref.setBuiltin(Void, loc);
ref.addPointer();
VarDecl* decl = p.builder.actOnVarDecl(name, loc, &ref, loc, fexpr, false, false);
return p.builder.actOnDeclStmt(&decl, 1);
}

fn FunctionDecl* Parser.parseFuncDecl(Parser* p, bool is_public, bool is_local) {
u32 func_name = 0;
SrcLoc func_loc = p.tok.loc;
Ref prefix_ref;
Ref* prefix = nil;

p.consumeToken();

TypeRefHolder rtype.init();
// Note: dont check arrays in this phase, but in Analyser
p.parseTypeSpecifier(&rtype);

p.expectIdentifier();
u32 func_name = p.tok.name_idx;
SrcLoc func_loc = p.tok.loc;
p.consumeToken();

Ref prefix_ref;
Ref* prefix = nil;
if (p.tok.kind == Kind.Dot) {
p.consumeToken();
if (p.tok.kind == Kind.Identifier || !is_local) {
p.expectIdentifier();

prefix_ref.loc = func_loc;
prefix_ref.name_idx = func_name;
prefix_ref.decl = nil;
prefix = &prefix_ref;
func_name = p.tok.name_idx;
func_loc = p.tok.loc;
p.consumeToken();
}

if (!p.checkName(func_name, p.is_interface)) {
p.errorAt(func_loc, "a function name must start with a lower case character");
if (p.tok.kind == Kind.Dot) {
if (is_local) {
p.error("local functions cannot be type functions");
}
p.consumeToken();
p.expectIdentifier();

prefix_ref.loc = func_loc;
prefix_ref.name_idx = func_name;
prefix_ref.decl = nil;
prefix = &prefix_ref;
func_name = p.tok.name_idx;
func_loc = p.tok.loc;
p.consumeToken();
}

if (!p.checkName(func_name, p.is_interface)) {
p.errorAt(func_loc, "a function name must start with a lower case character");
}
}

DeclList params.init();

bool is_variadic = p.parseFunctionParams(&params, is_public, true);

if (p.tok.kind == Kind.LSquare && is_local) {
// TODO: parse capture specification
while (!p.tok.done && p.tok.kind != Kind.RSquare) {
p.consumeToken();
}
p.expectAndConsume(Kind.RSquare);
}

FunctionDecl* f;
if (p.tok.kind == Kind.KW_template) {
if (is_local) {
p.error("local functions cannot be template functions");
}
p.consumeToken();
p.expectIdentifier();

Expand All @@ -515,28 +569,31 @@ fn void Parser.parseFuncDecl(Parser* p, bool is_public) {
prefix,
(VarDecl**)params.getDecls(),
params.size(),
is_variadic);
is_variadic, is_local);
}

params.free();

p.parseOptionalAttributes();
p.builder.applyAttributes((Decl*)f);
if (!is_local) {
p.parseOptionalAttributes();
p.builder.applyAttributes((Decl*)f);
}

if (p.is_interface) {
if (p.tok.kind == Kind.Semicolon) {
// function without body (eg. i32 printf(..); ) is allowed
p.consumeToken();
return;
return f;
}

// mark functions with bodies in interface files as 'inline'
f.setAttrInline();
}

p.stmt_list_count = 0;
if (!is_local) p.stmt_list_count = 0;
CompoundStmt* body = p.parseCompoundStmt();
p.builder.actOnFunctionBody(f, body);
return f;
}

fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, bool accept_default) {
Expand Down
4 changes: 4 additions & 0 deletions parser/c2_parser_expr.c2
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ const u8[128] CastExprTokenLookup = {
[Kind.KW_usize] = 16,
[Kind.KW_f32] = 16,
[Kind.KW_f64] = 16,
[Kind.KW_fn] = 17,
}

// Note: tried lookup table, was slower..
Expand Down Expand Up @@ -324,6 +325,9 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp
p.error("expected expression");
}
break;
case 17: // KW_fn
res = p.parseLocalFuncExpr();
break;
}
return p.parsePostfixExprSuffix(res, couldBeTemplateCall);
}
Expand Down
2 changes: 2 additions & 0 deletions parser/c2_parser_stmt.c2
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ fn Stmt* Parser.parseStmt(Parser* p) {
return p.parseDeclStmt(true, true, false);
case KW_while:
return p.parseWhileStmt();
case KW_fn:
return p.parseLocalFuncStmt();
default:
return p.parseExprStmt();
}
Expand Down
2 changes: 1 addition & 1 deletion test/parser/invalid_func.c2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module test;

public fn i32 main() {
fn void() v = foo; // @error{expected expression}
fn void() v = foo; // @error{expected '{'}
return 0;
}