Commit c627f9ea18
Changed files (7)
doc/langref.html.in
@@ -5819,9 +5819,11 @@ TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
ErrorValueDecl = "error" Symbol ";"
-GlobalVarDecl = VariableDeclaration ";"
+GlobalVarDecl = option("export") VariableDeclaration ";"
-VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") option("section" "(" Expression ")") "=" Expression
+LocalVarDecl = option("comptime") VariableDeclaration
+
+VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") option("section" "(" Expression ")") "=" Expression
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
@@ -5831,9 +5833,9 @@ UseDecl = "use" Expression ";"
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
-FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
+FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
-FnDef = option("inline" | "extern") FnProto Block
+FnDef = option("inline" | "export") FnProto Block
ParamDeclList = "(" list(ParamDecl, ",") ")"
@@ -5841,7 +5843,7 @@ ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")
Block = "{" many(Statement) option(Expression) "}"
-Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
+Statement = Label | LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
Label = Symbol ":"
@@ -5947,7 +5949,7 @@ StructLiteralField = "." Symbol "=" Expression
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
src/all_types.hpp
@@ -315,12 +315,6 @@ struct TldVar {
VariableTableEntry *var;
Buf *extern_lib_name;
Buf *section_name;
-
- size_t export_count;
- union {
- TldExport *tld; // if export_count == 1
- TldExport **tld_list; // if export_count > 1
- } export_data;
};
struct TldFn {
@@ -428,6 +422,7 @@ struct AstNodeFnProto {
AstNode *return_type;
bool is_var_args;
bool is_extern;
+ bool is_export;
bool is_inline;
CallingConvention cc;
AstNode *fn_def_node;
@@ -485,7 +480,8 @@ struct AstNodeVariableDeclaration {
VisibMod visib_mod;
Buf *symbol;
bool is_const;
- bool is_inline;
+ bool is_comptime;
+ bool is_export;
bool is_extern;
// one or both of type and expr will be non null
AstNode *type;
src/analyze.cpp
@@ -1062,7 +1062,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
if (fn_proto->cc == CallingConventionUnspecified) {
- bool extern_abi = fn_proto->is_extern;
+ bool extern_abi = fn_proto->is_extern || fn_proto->is_export;
fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified;
} else {
fn_type_id->cc = fn_proto->cc;
@@ -2675,6 +2675,26 @@ static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
}
static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
+ bool is_export = false;
+ if (tld->id == TldIdVar) {
+ assert(tld->source_node->type == NodeTypeVariableDeclaration);
+ is_export = tld->source_node->data.variable_declaration.is_export;
+ } else if (tld->id == TldIdFn) {
+ assert(tld->source_node->type == NodeTypeFnProto);
+ is_export = tld->source_node->data.fn_proto.is_export;
+ }
+ if (is_export) {
+ g->resolve_queue.append(tld);
+
+ auto entry = g->exported_symbol_names.put_unique(tld->name, tld->source_node);
+ if (entry) {
+ AstNode *other_source_node = entry->value;
+ ErrorMsg *msg = add_node_error(g, tld->source_node,
+ buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name)));
+ add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here"));
+ }
+ }
+
{
auto entry = decls_scope->decl_table.put_unique(tld->name, tld);
if (entry) {
@@ -3006,6 +3026,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
bool is_const = var_decl->is_const;
bool is_extern = var_decl->is_extern;
+ bool is_export = var_decl->is_export;
TypeTableEntry *explicit_type = nullptr;
if (var_decl->type) {
@@ -3013,8 +3034,12 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
explicit_type = validate_var_type(g, var_decl->type, proposed_type);
}
+ assert(!is_export || !is_extern);
+
VarLinkage linkage;
- if (is_extern) {
+ if (is_export) {
+ linkage = VarLinkageExport;
+ } else if (is_extern) {
linkage = VarLinkageExternal;
} else {
linkage = VarLinkageInternal;
src/ir.cpp
@@ -5066,7 +5066,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
bool is_const = variable_declaration->is_const;
bool is_extern = variable_declaration->is_extern;
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
- ir_should_inline(irb->exec, scope) || variable_declaration->is_inline);
+ ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime);
VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
is_const, is_const, is_shadowable, is_comptime);
// we detect IrInstructionIdDeclVar in gen_block to make sure the next node
src/parser.cpp
@@ -676,7 +676,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
}
/*
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@@ -791,13 +791,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
if (container_decl)
return container_decl;
- if (token->id == TokenIdKeywordExtern) {
- *token_index += 1;
- AstNode *node = ast_parse_fn_proto(pc, token_index, true, VisibModPrivate);
- node->data.fn_proto.is_extern = true;
- return node;
- }
-
if (!mandatory)
return nullptr;
@@ -1534,38 +1527,20 @@ static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) {
}
/*
-VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
+VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
*/
static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory,
- VisibMod visib_mod)
+ VisibMod visib_mod, bool is_comptime, bool is_export)
{
Token *first_token = &pc->tokens->at(*token_index);
Token *var_token;
bool is_const;
- bool is_comptime;
- if (first_token->id == TokenIdKeywordCompTime) {
- is_comptime = true;
- var_token = &pc->tokens->at(*token_index + 1);
-
- if (var_token->id == TokenIdKeywordVar) {
- is_const = false;
- } else if (var_token->id == TokenIdKeywordConst) {
- is_const = true;
- } else if (mandatory) {
- ast_invalid_token_error(pc, var_token);
- } else {
- return nullptr;
- }
-
- *token_index += 2;
- } else if (first_token->id == TokenIdKeywordVar) {
- is_comptime = false;
+ if (first_token->id == TokenIdKeywordVar) {
is_const = false;
var_token = first_token;
*token_index += 1;
} else if (first_token->id == TokenIdKeywordConst) {
- is_comptime = false;
is_const = true;
var_token = first_token;
*token_index += 1;
@@ -1577,7 +1552,7 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token);
- node->data.variable_declaration.is_inline = is_comptime;
+ node->data.variable_declaration.is_comptime = is_comptime;
node->data.variable_declaration.is_const = is_const;
node->data.variable_declaration.visib_mod = visib_mod;
@@ -1620,6 +1595,50 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
return node;
}
+/*
+GlobalVarDecl = option("export") VariableDeclaration ";"
+*/
+static AstNode *ast_parse_global_var_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) {
+ Token *first_token = &pc->tokens->at(*token_index);
+
+ bool is_export = false;;
+ if (first_token->id == TokenIdKeywordExport) {
+ *token_index += 1;
+ is_export = true;
+ }
+
+ AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, is_export);
+ if (node == nullptr) {
+ if (is_export) {
+ *token_index -= 1;
+ }
+ return nullptr;
+ }
+ return node;
+}
+
+/*
+LocalVarDecl = option("comptime") VariableDeclaration
+*/
+static AstNode *ast_parse_local_var_decl(ParseContext *pc, size_t *token_index) {
+ Token *first_token = &pc->tokens->at(*token_index);
+
+ bool is_comptime = false;;
+ if (first_token->id == TokenIdKeywordCompTime) {
+ *token_index += 1;
+ is_comptime = true;
+ }
+
+ AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate, is_comptime, false);
+ if (node == nullptr) {
+ if (is_comptime) {
+ *token_index -= 1;
+ }
+ return nullptr;
+ }
+ return node;
+}
+
/*
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
*/
@@ -2171,7 +2190,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
for (;;) {
AstNode *statement_node = ast_parse_label(pc, token_index, false);
if (!statement_node)
- statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
+ statement_node = ast_parse_local_var_decl(pc, token_index);
if (!statement_node)
statement_node = ast_parse_defer_expr(pc, token_index);
if (!statement_node)
@@ -2213,13 +2232,14 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
}
/*
-FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
+FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
*/
static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
Token *first_token = &pc->tokens->at(*token_index);
Token *fn_token;
CallingConvention cc;
+ bool is_extern = false;
if (first_token->id == TokenIdKeywordColdCC) {
*token_index += 1;
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
@@ -2232,8 +2252,13 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
*token_index += 1;
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
cc = CallingConventionStdcall;
+ } else if (first_token->id == TokenIdKeywordExtern) {
+ *token_index += 1;
+ fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
+ cc = CallingConventionC;
} else if (first_token->id == TokenIdKeywordFn) {
fn_token = first_token;
+ is_extern = true;
*token_index += 1;
cc = CallingConventionUnspecified;
} else if (mandatory) {
@@ -2246,6 +2271,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
node->data.fn_proto.visib_mod = visib_mod;
node->data.fn_proto.cc = cc;
+ node->data.fn_proto.is_extern = is_extern;
Token *fn_name = &pc->tokens->at(*token_index);
@@ -2286,35 +2312,35 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
}
/*
-FnDef = option("inline" | "extern") FnProto Block
+FnDef = option("inline" | "export") FnProto Block
*/
static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
Token *first_token = &pc->tokens->at(*token_index);
bool is_inline;
- bool is_extern;
+ bool is_export;
if (first_token->id == TokenIdKeywordInline) {
*token_index += 1;
is_inline = true;
- is_extern = false;
- } else if (first_token->id == TokenIdKeywordExtern) {
+ is_export = false;
+ } else if (first_token->id == TokenIdKeywordExport) {
*token_index += 1;
- is_extern = true;
+ is_export = true;
is_inline = false;
} else {
is_inline = false;
- is_extern = false;
+ is_export = false;
}
AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod);
if (!fn_proto) {
- if (is_inline || is_extern) {
+ if (is_inline || is_export) {
*token_index -= 1;
}
return nullptr;
}
fn_proto->data.fn_proto.is_inline = is_inline;
- fn_proto->data.fn_proto.is_extern = is_extern;
+ fn_proto->data.fn_proto.is_export = is_export;
Token *semi_token = &pc->tokens->at(*token_index);
if (semi_token->id == TokenIdSemicolon) {
@@ -2360,7 +2386,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo
return fn_proto_node;
}
- AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
+ AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, false);
if (var_decl_node) {
ast_eat_token(pc, token_index, TokenIdSemicolon);
@@ -2473,7 +2499,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
continue;
}
- AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
+ AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
if (var_decl_node) {
ast_eat_token(pc, token_index, TokenIdSemicolon);
node->data.container_decl.decls.append(var_decl_node);
@@ -2566,7 +2592,7 @@ static AstNode *ast_parse_test_decl_node(ParseContext *pc, size_t *token_index)
/*
TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
-TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
+TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
*/
static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, ZigList<AstNode *> *top_level_decls) {
for (;;) {
@@ -2615,7 +2641,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
continue;
}
- AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
+ AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
if (var_decl_node) {
ast_eat_token(pc, token_index, TokenIdSemicolon);
top_level_decls->append(var_decl_node);
src/tokenizer.cpp
@@ -119,6 +119,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"else", TokenIdKeywordElse},
{"enum", TokenIdKeywordEnum},
{"error", TokenIdKeywordError},
+ {"export", TokenIdKeywordExport},
{"extern", TokenIdKeywordExtern},
{"false", TokenIdKeywordFalse},
{"fn", TokenIdKeywordFn},
@@ -1518,6 +1519,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordElse: return "else";
case TokenIdKeywordEnum: return "enum";
case TokenIdKeywordError: return "error";
+ case TokenIdKeywordExport: return "export";
case TokenIdKeywordExtern: return "extern";
case TokenIdKeywordFalse: return "false";
case TokenIdKeywordFn: return "fn";
src/tokenizer.hpp
@@ -59,6 +59,7 @@ enum TokenId {
TokenIdKeywordElse,
TokenIdKeywordEnum,
TokenIdKeywordError,
+ TokenIdKeywordExport,
TokenIdKeywordExtern,
TokenIdKeywordFalse,
TokenIdKeywordFn,