fix usingnamespace

It used to be that usingnamespace was only allowed at top level. This
made it OK to put the state inside the AST node data structure. However,
now usingnamespace can occur inside any aggregate data structure, and
therefore the state must be in the TopLevelDeclaration rather than in
the AST node.

There were two other problems with the usingnamespace implementation:

 * It was passing the wrong destination ScopeDecl, so it could cause an
   incorrect error such as "import of file outside package path".
 * When doing `usingnamespace` on a file that already had
   `pub usingnamespace` in it would "steal" the usingnamespace, causing
   incorrect "use of undeclared identifier" errors in the target file.

closes #2632
closes #2580
This commit is contained in:
Andrew Kelley 2019-07-19 16:56:44 -04:00
parent 1d07bbbef2
commit af8661405b
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
9 changed files with 170 additions and 145 deletions

View File

@ -366,6 +366,7 @@ enum TldId {
TldIdFn,
TldIdContainer,
TldIdCompTime,
TldIdUsingNamespace,
};
enum TldResolution {
@ -413,6 +414,12 @@ struct TldCompTime {
Tld base;
};
struct TldUsingNamespace {
Tld base;
ConstExprValue *using_namespace_value;
};
struct TypeEnumField {
Buf *name;
BigInt value;
@ -453,7 +460,7 @@ enum NodeType {
NodeTypeFieldAccessExpr,
NodeTypePtrDeref,
NodeTypeUnwrapOptional,
NodeTypeUse,
NodeTypeUsingNamespace,
NodeTypeBoolLiteral,
NodeTypeNullLiteral,
NodeTypeUndefinedLiteral,
@ -715,9 +722,6 @@ struct AstNodeArrayType {
struct AstNodeUsingNamespace {
VisibMod visib_mod;
AstNode *expr;
TldResolution resolution;
ConstExprValue *using_namespace_value;
};
struct AstNodeIfBoolExpr {
@ -1745,8 +1749,6 @@ struct CodeGen {
ZigList<Tld *> resolve_queue;
size_t resolve_queue_index;
ZigList<AstNode *> use_queue;
size_t use_queue_index;
ZigList<TimeEvent> timing_events;
ZigList<AstNode *> tld_ref_source_node_stack;
ZigList<ZigFn *> inline_fns;
@ -2005,7 +2007,7 @@ struct ScopeDecls {
Scope base;
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table;
ZigList<AstNode *> use_decls;
ZigList<TldUsingNamespace *> use_decls;
AstNode *safety_set_node;
AstNode *fast_math_set_node;
ZigType *import;

View File

@ -28,6 +28,8 @@ static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *uni
static Error ATTRIBUTE_MUST_USE resolve_union_alignment(CodeGen *g, ZigType *union_type);
static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status);
static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope);
static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope);
static bool is_top_level_struct(ZigType *import) {
return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr;
@ -2854,6 +2856,8 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body"));
return;
}
} else if (tld->id == TldIdUsingNamespace) {
g->resolve_queue.append(tld);
}
if (is_export) {
g->resolve_queue.append(tld);
@ -2867,7 +2871,7 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
}
}
{
if (tld->name != nullptr) {
auto entry = decls_scope->decl_table.put_unique(tld->name, tld);
if (entry) {
Tld *other_tld = entry->value;
@ -2875,9 +2879,7 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here"));
return;
}
}
{
ZigType *type;
if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) {
add_node_error(g, tld->source_node,
@ -2977,12 +2979,14 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
break;
}
case NodeTypeUse:
{
g->use_queue.append(node);
decls_scope->use_decls.append(node);
break;
}
case NodeTypeUsingNamespace: {
VisibMod visib_mod = node->data.using_namespace.visib_mod;
TldUsingNamespace *tld_using_namespace = allocate<TldUsingNamespace>(1);
init_tld(&tld_using_namespace->base, TldIdUsingNamespace, nullptr, visib_mod, node, &decls_scope->base);
add_top_level_decl(g, decls_scope, &tld_using_namespace->base);
decls_scope->use_decls.append(tld_using_namespace);
break;
}
case NodeTypeTestDecl:
preview_test_decl(g, node, decls_scope);
break;
@ -3266,6 +3270,117 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
g->global_vars.append(tld_var);
}
static void add_symbols_from_container(CodeGen *g, TldUsingNamespace *src_using_namespace,
TldUsingNamespace *dst_using_namespace, ScopeDecls* dest_decls_scope)
{
if (src_using_namespace->base.resolution == TldResolutionUnresolved ||
src_using_namespace->base.resolution == TldResolutionResolving)
{
assert(src_using_namespace->base.parent_scope->id == ScopeIdDecls);
ScopeDecls *src_decls_scope = (ScopeDecls *)src_using_namespace->base.parent_scope;
preview_use_decl(g, src_using_namespace, src_decls_scope);
if (src_using_namespace != dst_using_namespace) {
resolve_use_decl(g, src_using_namespace, src_decls_scope);
}
}
ConstExprValue *use_expr = src_using_namespace->using_namespace_value;
if (type_is_invalid(use_expr->type)) {
dest_decls_scope->any_imports_failed = true;
return;
}
dst_using_namespace->base.resolution = TldResolutionOk;
assert(use_expr->special != ConstValSpecialRuntime);
// The source scope for the imported symbols
ScopeDecls *src_scope = get_container_scope(use_expr->data.x_type);
// The top-level container where the symbols are defined, it's used in the
// loop below in order to exclude the ones coming from an import statement
ZigType *src_import = get_scope_import(&src_scope->base);
assert(src_import != nullptr);
if (src_scope->any_imports_failed) {
dest_decls_scope->any_imports_failed = true;
}
auto it = src_scope->decl_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
Buf *target_tld_name = entry->key;
Tld *target_tld = entry->value;
if (target_tld->visib_mod == VisibModPrivate) {
continue;
}
if (target_tld->import != src_import) {
continue;
}
auto existing_entry = dest_decls_scope->decl_table.put_unique(target_tld_name, target_tld);
if (existing_entry) {
Tld *existing_decl = existing_entry->value;
if (existing_decl != target_tld) {
ErrorMsg *msg = add_node_error(g, dst_using_namespace->base.source_node,
buf_sprintf("import of '%s' overrides existing definition",
buf_ptr(target_tld_name)));
add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here"));
add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here"));
}
}
}
for (size_t i = 0; i < src_scope->use_decls.length; i += 1) {
TldUsingNamespace *tld_using_namespace = src_scope->use_decls.at(i);
if (tld_using_namespace->base.visib_mod != VisibModPrivate)
add_symbols_from_container(g, tld_using_namespace, dst_using_namespace, dest_decls_scope);
}
}
static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope) {
if (tld_using_namespace->base.resolution == TldResolutionOk ||
tld_using_namespace->base.resolution == TldResolutionInvalid)
{
return;
}
add_symbols_from_container(g, tld_using_namespace, tld_using_namespace, dest_decls_scope);
}
static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope) {
if (using_namespace->base.resolution == TldResolutionOk ||
using_namespace->base.resolution == TldResolutionInvalid)
{
return;
}
using_namespace->base.resolution = TldResolutionResolving;
assert(using_namespace->base.source_node->type == NodeTypeUsingNamespace);
ConstExprValue *result = analyze_const_value(g, &dest_decls_scope->base,
using_namespace->base.source_node->data.using_namespace.expr, g->builtin_types.entry_type, nullptr);
using_namespace->using_namespace_value = result;
if (type_is_invalid(result->type)) {
dest_decls_scope->any_imports_failed = true;
using_namespace->base.resolution = TldResolutionInvalid;
using_namespace->using_namespace_value = &g->invalid_instruction->value;
return;
}
if (!is_container(result->data.x_type)) {
add_node_error(g, using_namespace->base.source_node,
buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&result->data.x_type->name)));
dest_decls_scope->any_imports_failed = true;
using_namespace->base.resolution = TldResolutionInvalid;
using_namespace->using_namespace_value = &g->invalid_instruction->value;
return;
}
}
void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) {
if (tld->resolution != TldResolutionUnresolved)
return;
@ -3299,6 +3414,14 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) {
resolve_decl_comptime(g, tld_comptime);
break;
}
case TldIdUsingNamespace: {
TldUsingNamespace *tld_using_namespace = (TldUsingNamespace *)tld;
assert(tld_using_namespace->base.parent_scope->id == ScopeIdDecls);
ScopeDecls *dest_decls_scope = (ScopeDecls *)tld_using_namespace->base.parent_scope;
preview_use_decl(g, tld_using_namespace, dest_decls_scope);
resolve_use_decl(g, tld_using_namespace, dest_decls_scope);
break;
}
}
tld->resolution = TldResolutionOk;
@ -3308,10 +3431,10 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) {
Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) {
// resolve all the using_namespace decls
for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) {
AstNode *use_decl_node = decls_scope->use_decls.at(i);
if (use_decl_node->data.using_namespace.resolution == TldResolutionUnresolved) {
preview_use_decl(g, use_decl_node, decls_scope);
resolve_use_decl(g, use_decl_node, decls_scope);
TldUsingNamespace *tld_using_namespace = decls_scope->use_decls.at(i);
if (tld_using_namespace->base.resolution == TldResolutionUnresolved) {
preview_use_decl(g, tld_using_namespace, decls_scope);
resolve_use_decl(g, tld_using_namespace, decls_scope);
}
}
@ -3752,110 +3875,6 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
analyze_fn_ir(g, fn_table_entry, return_type_node);
}
static void add_symbols_from_container(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node, ScopeDecls* decls_scope) {
if (src_use_node->data.using_namespace.resolution == TldResolutionUnresolved) {
preview_use_decl(g, src_use_node, decls_scope);
}
ConstExprValue *use_expr = src_use_node->data.using_namespace.using_namespace_value;
if (type_is_invalid(use_expr->type)) {
decls_scope->any_imports_failed = true;
return;
}
dst_use_node->data.using_namespace.resolution = TldResolutionOk;
assert(use_expr->special != ConstValSpecialRuntime);
// The source struct for the imported symbols
ZigType *src_ty = use_expr->data.x_type;
assert(src_ty);
if (!is_container(src_ty)) {
add_node_error(g, dst_use_node,
buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&src_ty->name)));
decls_scope->any_imports_failed = true;
return;
}
// The source scope for the imported symbols
ScopeDecls *src_scope = get_container_scope(src_ty);
// The top-level container where the symbols are defined, it's used in the
// loop below in order to exclude the ones coming from an import statement
ZigType *src_import = get_scope_import(&src_scope->base);
assert(src_import != nullptr);
if (src_scope->any_imports_failed) {
decls_scope->any_imports_failed = true;
}
auto it = src_scope->decl_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
Buf *target_tld_name = entry->key;
Tld *target_tld = entry->value;
if (target_tld->visib_mod == VisibModPrivate) {
continue;
}
if (target_tld->import != src_import) {
continue;
}
auto existing_entry = decls_scope->decl_table.put_unique(target_tld_name, target_tld);
if (existing_entry) {
Tld *existing_decl = existing_entry->value;
if (existing_decl != target_tld) {
ErrorMsg *msg = add_node_error(g, dst_use_node,
buf_sprintf("import of '%s' overrides existing definition",
buf_ptr(target_tld_name)));
add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here"));
add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here"));
}
}
}
for (size_t i = 0; i < src_scope->use_decls.length; i += 1) {
AstNode *use_decl_node = src_scope->use_decls.at(i);
if (use_decl_node->data.using_namespace.visib_mod != VisibModPrivate)
add_symbols_from_container(g, use_decl_node, dst_use_node, decls_scope);
}
}
void resolve_use_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
assert(node->type == NodeTypeUse);
if (node->data.using_namespace.resolution == TldResolutionOk ||
node->data.using_namespace.resolution == TldResolutionInvalid)
{
return;
}
add_symbols_from_container(g, node, node, decls_scope);
}
void preview_use_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
assert(node->type == NodeTypeUse);
if (node->data.using_namespace.resolution == TldResolutionOk ||
node->data.using_namespace.resolution == TldResolutionInvalid)
{
return;
}
node->data.using_namespace.resolution = TldResolutionResolving;
ConstExprValue *result = analyze_const_value(g, &decls_scope->base,
node->data.using_namespace.expr, g->builtin_types.entry_type, nullptr);
if (type_is_invalid(result->type))
decls_scope->any_imports_failed = true;
node->data.using_namespace.using_namespace_value = result;
}
ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code,
SourceKind source_kind)
{
@ -3975,18 +3994,8 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu
void semantic_analyze(CodeGen *g) {
while (g->resolve_queue_index < g->resolve_queue.length ||
g->fn_defs_index < g->fn_defs.length ||
g->use_queue_index < g->use_queue.length)
g->fn_defs_index < g->fn_defs.length)
{
for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) {
AstNode *use_decl_node = g->use_queue.at(g->use_queue_index);
// Get the top-level scope where `using_namespace` is used
ScopeDecls *decls_scope = get_container_scope(use_decl_node->owner);
if (use_decl_node->data.using_namespace.resolution == TldResolutionUnresolved) {
preview_use_decl(g, use_decl_node, decls_scope);
resolve_use_decl(g, use_decl_node, decls_scope);
}
}
for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
AstNode *source_node = nullptr;

View File

@ -86,8 +86,6 @@ bool is_array_ref(ZigType *type_entry);
bool is_container_ref(ZigType *type_entry);
bool is_valid_vector_elem_type(ZigType *elem_type);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void preview_use_decl(CodeGen *g, AstNode *node, ScopeDecls* decls_scope);
void resolve_use_decl(CodeGen *g, AstNode *node, ScopeDecls* decls_scope);
ZigFn *scope_fn_entry(Scope *scope);
ZigPackage *scope_package(Scope *scope);
ZigType *get_scope_import(Scope *scope);

View File

@ -193,8 +193,8 @@ static const char *node_type_str(NodeType node_type) {
return "Symbol";
case NodeTypePrefixOpExpr:
return "PrefixOpExpr";
case NodeTypeUse:
return "Use";
case NodeTypeUsingNamespace:
return "UsingNamespace";
case NodeTypeBoolLiteral:
return "BoolLiteral";
case NodeTypeNullLiteral:
@ -791,7 +791,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
AstNode *decls_node = node->data.container_decl.decls.at(decl_i);
render_node_grouped(ar, decls_node);
if (decls_node->type == NodeTypeUse ||
if (decls_node->type == NodeTypeUsingNamespace ||
decls_node->type == NodeTypeVariableDeclaration ||
decls_node->type == NodeTypeFnProto)
{
@ -1170,7 +1170,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeParamDecl:
case NodeTypeTestDecl:
case NodeTypeStructField:
case NodeTypeUse:
case NodeTypeUsingNamespace:
zig_panic("TODO more ast rendering");
}
}

View File

@ -8430,7 +8430,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
switch (node->type) {
case NodeTypeStructValueField:
case NodeTypeParamDecl:
case NodeTypeUse:
case NodeTypeUsingNamespace:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
@ -17847,6 +17847,7 @@ static IrInstruction *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_
switch (tld->id) {
case TldIdContainer:
case TldIdCompTime:
case TldIdUsingNamespace:
zig_unreachable();
case TldIdVar:
{

View File

@ -676,7 +676,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
AstNode *expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdSemicolon);
AstNode *res = ast_create_node(pc, NodeTypeUse, usingnamespace);
AstNode *res = ast_create_node(pc, NodeTypeUsingNamespace, usingnamespace);
res->data.using_namespace.visib_mod = visib_mod;
res->data.using_namespace.expr = expr;
return res;
@ -2938,7 +2938,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
case NodeTypeUnwrapOptional:
visit_field(&node->data.unwrap_optional.expr, visit, context);
break;
case NodeTypeUse:
case NodeTypeUsingNamespace:
visit_field(&node->data.using_namespace.expr, visit, context);
break;
case NodeTypeBoolLiteral:

View File

@ -234,7 +234,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"usingnamespace with wrong type",
\\use void;
\\usingnamespace void;
,
"tmp.zig:1:1: error: expected struct, enum, or union; found 'void'",
);

View File

@ -93,6 +93,7 @@ comptime {
_ = @import("behavior/undefined.zig");
_ = @import("behavior/underscore.zig");
_ = @import("behavior/union.zig");
_ = @import("behavior/usingnamespace.zig");
_ = @import("behavior/var_args.zig");
_ = @import("behavior/vector.zig");
_ = @import("behavior/void.zig");

View File

@ -0,0 +1,14 @@
const std = @import("std");
fn Foo(comptime T: type) type {
return struct {
usingnamespace T;
};
}
test "usingnamespace inside a generic struct" {
const std2 = Foo(std);
const testing2 = Foo(std.testing);
std2.testing.expect(true);
testing2.expect(true);
}