Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix modules with same name as builtins causing ICE (#3315) #3437

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
12 changes: 12 additions & 0 deletions gcc/rust/resolve/rust-forever-stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ template <Namespace N> class ForeverStack
// FIXME: Is that valid? Do we use the root? If yes, we should give the
// crate's node id to ForeverStack's constructor
: root (Node (Rib (Rib::Kind::Normal), UNKNOWN_NODEID)),
prelude (Node (Rib (Rib::Kind::Prelude), UNKNOWN_NODEID, root)),
cursor_reference (root)
{
rust_assert (root.is_root ());
Expand Down Expand Up @@ -651,6 +652,8 @@ template <Namespace N> class ForeverStack
* the current map, an empty one otherwise.
*/
tl::optional<Rib::Definition> get (const Identifier &name);
tl::optional<Rib::Definition> get_prelude (const Identifier &name);
tl::optional<Rib::Definition> get_prelude (const std::string &name);

/**
* Resolve a path to its definition in the current `ForeverStack`
Expand Down Expand Up @@ -715,6 +718,7 @@ template <Namespace N> class ForeverStack
{}

bool is_root () const;
bool is_prelude () const;
bool is_leaf () const;

void insert_child (Link link, Node child);
Expand Down Expand Up @@ -750,7 +754,15 @@ template <Namespace N> class ForeverStack
const Node &cursor () const;
void update_cursor (Node &new_cursor);

/* The forever stack's actual nodes */
Node root;
/*
* A special prelude node used currently for resolving language builtins
* It has the root node as a parent, and acts as a "special case" for name
* resolution
*/
Node prelude;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably should have noticed and commented on this earlier, but this should really be called something like language_prelude, given that we'll have to deal with multiple preludes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that as well, I figured that we would also dump the std::prelude and core::prelude in here as well

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured we'd keep some sort of reference to the standard library prelude -- since that should already be a module with its own Node, we shouldn't need to try to copy entries into another Node

Copy link
Contributor Author

@liamnaddell liamnaddell Mar 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment there clarifies that it's currently only used for the language prelude (language builtins). If it needs to be beefed up with a better explanation I can do that.


std::reference_wrapper<Node> cursor_reference;

void stream_rib (std::stringstream &stream, const Rib &rib,
Expand Down
82 changes: 72 additions & 10 deletions gcc/rust/resolve/rust-forever-stack.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ ForeverStack<N>::Node::is_root () const
return !parent.has_value ();
}

template <Namespace N>
bool
ForeverStack<N>::Node::is_prelude () const
{
return rib.kind == Rib::Kind::Prelude;
}

template <Namespace N>
bool
ForeverStack<N>::Node::is_leaf () const
Expand Down Expand Up @@ -63,6 +70,16 @@ template <Namespace N>
void
ForeverStack<N>::push_inner (Rib rib, Link link)
{
if (rib.kind == Rib::Kind::Prelude)
{
// If you push_inner into the prelude from outside the root, you will pop
// back into the root, which could screw up a traversal.
rust_assert (&cursor_reference.get () == &root);
// Prelude doesn't have an access path
rust_assert (!link.path);
update_cursor (this->prelude);
return;
}
// If the link does not exist, we create it and emplace a new `Node` with the
// current node as its parent. `unordered_map::emplace` returns a pair with
// the iterator and a boolean. If the value already exists, the iterator
Expand Down Expand Up @@ -290,6 +307,20 @@ ForeverStack<N>::get (const Identifier &name)
return resolved_definition;
}

template <Namespace N>
tl::optional<Rib::Definition>
ForeverStack<N>::get_prelude (const Identifier &name)
{
return prelude.rib.get (name.as_string ());
}

template <Namespace N>
tl::optional<Rib::Definition>
ForeverStack<N>::get_prelude (const std::string &name)
{
return prelude.rib.get (name);
}

template <>
tl::optional<Rib::Definition> inline ForeverStack<Namespace::Labels>::get (
const Identifier &name)
Expand Down Expand Up @@ -389,7 +420,7 @@ ForeverStack<N>::find_starting_point (
break;

auto &seg = unwrap_type_segment (outer_seg);
auto is_self_or_crate
bool is_self_or_crate
= seg.is_crate_path_seg () || seg.is_lower_self_seg ();

// if we're after the first path segment and meet `self` or `crate`, it's
Expand Down Expand Up @@ -452,7 +483,7 @@ ForeverStack<N>::resolve_segments (
typename std::vector<S>::const_iterator iterator,
std::function<void (const S &, NodeId)> insert_segment_resolution)
{
auto *current_node = &starting_point;
Node *current_node = &starting_point;
for (; !is_last (iterator, segments); iterator++)
{
auto &outer_seg = *iterator;
Expand All @@ -468,7 +499,7 @@ ForeverStack<N>::resolve_segments (
}

auto &seg = unwrap_type_segment (outer_seg);
auto str = seg.as_string ();
std::string str = seg.as_string ();
rust_debug ("[ARTHUR]: resolving segment part: %s", str.c_str ());

// check that we don't encounter *any* leading keywords afterwards
Expand All @@ -483,10 +514,20 @@ ForeverStack<N>::resolve_segments (
* On every iteration this loop either
*
* 1. terminates
* 2. decreases the depth of the node pointed to by current_node
*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably add an entry to the numbered list in the above comment, mentioning changing searched_prelude from false to true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment for this, let me know if it needs modification.

* This ensures termination
* 2. decreases the depth of the node pointed to by current_node until
* current_node reaches the root
*
* 3. If the root node is reached, and we were not able to resolve the
* segment, we search the prelude rib for the segment, by setting
* current_node to point to the prelude, and toggling the
* searched_prelude boolean to true. If current_node is the prelude
* rib, and searched_prelude is true, we will exit.
*
* This ensures termination.
*
*/
bool searched_prelude = false;
while (true)
{
// may set the value of child
Expand Down Expand Up @@ -522,9 +563,16 @@ ForeverStack<N>::resolve_segments (
}
}

if (current_node->is_root () && !searched_prelude)
{
searched_prelude = true;
current_node = &prelude;
continue;
}

if (!is_start (iterator, segments)
|| current_node->rib.kind == Rib::Kind::Module
|| current_node->is_root ())
|| current_node->is_prelude ())
{
return tl::nullopt;
}
Expand Down Expand Up @@ -564,7 +612,12 @@ ForeverStack<N>::resolve_path (
return Rib::Definition::NonShadowable (seg_id);
}

auto res = get (unwrap_type_segment (segments.back ()).as_string ());
tl::optional<Rib::Definition> res
= get (unwrap_type_segment (segments.back ()).as_string ());

if (!res)
res = get_prelude (unwrap_type_segment (segments.back ()).as_string ());

if (res && !res->is_ambiguous ())
insert_segment_resolution (segments.back (), res->get_node_id ());
return res;
Expand All @@ -579,16 +632,25 @@ ForeverStack<N>::resolve_path (
return resolve_segments (starting_point.get (), segments, iterator,
insert_segment_resolution);
})
.and_then ([&segments, &insert_segment_resolution] (
.and_then ([this, &segments, &insert_segment_resolution] (
Node final_node) -> tl::optional<Rib::Definition> {
// leave resolution within impl blocks to type checker
if (final_node.rib.kind == Rib::Kind::TraitOrImpl)
return tl::nullopt;

std::string seg_name
= unwrap_type_segment (segments.back ()).as_string ();

// assuming this can't be a lang item segment
auto res = final_node.rib.get (
unwrap_type_segment (segments.back ()).as_string ());
tl::optional<Rib::Definition> res = final_node.rib.get (seg_name);

// Ok we didn't find it in the rib, Lets try the prelude...
if (!res)
res = get_prelude (seg_name);

if (res && !res->is_ambiguous ())
insert_segment_resolution (segments.back (), res->get_node_id ());

return res;
});
}
Expand Down
33 changes: 18 additions & 15 deletions gcc/rust/resolve/rust-late-name-resolver-2.0.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,18 @@ Late::setup_builtin_types ()
// insert it in the type context...
};

for (const auto &builtin : builtins)
{
// we should be able to use `insert_at_root` or `insert` here, since we're
// at the root :) hopefully!
auto ok = ctx.types.insert (builtin.name, builtin.node_id);
rust_assert (ok);

ctx.mappings.insert_node_to_hir (builtin.node_id, builtin.hir_id);
ty_ctx.insert_builtin (builtin.hir_id, builtin.node_id, builtin.type);
}
// There's a special Rib for putting prelude items, since prelude items need
// to satisfy certain special rules.
ctx.scoped (Rib::Kind::Prelude, 0, [this, &ty_ctx] (void) -> void {
for (const auto &builtin : builtins)
{
auto ok = ctx.types.insert (builtin.name, builtin.node_id);
rust_assert (ok);

ctx.mappings.insert_node_to_hir (builtin.node_id, builtin.hir_id);
ty_ctx.insert_builtin (builtin.hir_id, builtin.node_id, builtin.type);
}
});

// ...here!
auto *unit_type = TyTy::TupleType::get_unit_type ();
Expand Down Expand Up @@ -213,7 +215,6 @@ Late::visit (AST::IdentifierExpr &expr)
// TODO: same thing as visit(PathInExpression) here?

tl::optional<Rib::Definition> resolved = tl::nullopt;

if (auto value = ctx.values.get (expr.get_ident ()))
{
resolved = value;
Expand All @@ -231,10 +232,12 @@ Late::visit (AST::IdentifierExpr &expr)
}
else
{
rust_error_at (expr.get_locus (),
"could not resolve identifier expression: %qs",
expr.get_ident ().as_string ().c_str ());
return;
if (auto typ = ctx.types.get_prelude (expr.get_ident ()))
resolved = typ;
else
rust_error_at (expr.get_locus (),
"could not resolve identifier expression: %qs",
expr.get_ident ().as_string ().c_str ());
}

ctx.map_usage (Usage (expr.get_node_id ()),
Expand Down
4 changes: 4 additions & 0 deletions gcc/rust/resolve/rust-name-resolution-context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, NodeId id,
std::function<void (void)> lambda,
tl::optional<Identifier> path)
{
// NOTE: You must be at the root node when pushing the prelude rib.
values.push (rib_kind, id, path);
types.push (rib_kind, id, path);
macros.push (rib_kind, id, path);
Expand All @@ -126,6 +127,9 @@ NameResolutionContext::scoped (Rib::Kind rib_kind, Namespace ns,
std::function<void (void)> lambda,
tl::optional<Identifier> path)
{
// This could work... I'm not sure why you would want to do this though.
rust_assert (rib_kind != Rib::Kind::Prelude);

switch (ns)
{
case Namespace::Values:
Expand Down
5 changes: 5 additions & 0 deletions gcc/rust/resolve/rust-rib.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ class Rib
ForwardTypeParamBan,
/* Const generic, as in the following example: fn foo<T, const X: T>() {} */
ConstParamType,
/* Prelude rib, used for both the language prelude (i32,usize,etc) and the
* (future) {std,core}::prelude::* import. A regular rib with the
* restriction that you cannot `use` items from the Prelude
*/
Prelude,
} kind;

Rib (Kind kind);
Expand Down
8 changes: 8 additions & 0 deletions gcc/testsuite/rust/compile/issue-3315-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//You should be able to create a module of the same name as a builtin type

mod i32 {
}

fn main() -> isize {
0
}
7 changes: 7 additions & 0 deletions gcc/testsuite/rust/compile/issue-3315-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod i32 {
}

fn main() -> isize {
let i:i32 = 0 as i32;
i as isize
}
1 change: 1 addition & 0 deletions gcc/testsuite/rust/compile/nr2/exclude
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ issue-2330.rs
issue-2812.rs
issue-850.rs
issue-855.rs
issue-3315-2.rs
iterators1.rs
lookup_err1.rs
macros/mbe/macro43.rs
Expand Down