aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2024-01-19 10:11:49 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2024-01-19 10:11:49 +0200
commitb56b9c6796d8853758f0f5967488260d61b788e2 (patch)
treec20c08a1ed94330101a25a7a626f5b523134f19b
parent85cf21db471a4ea9549f3b6f2384578b286dfc23 (diff)
Prevent inner self-typedefs from causing scope cyclesHEADmaster
-rw-r--r--odb/context.cxx25
-rw-r--r--odb/parser.cxx71
-rw-r--r--odb/processor.cxx22
-rw-r--r--odb/semantics/elements.cxx4
4 files changed, 107 insertions, 15 deletions
diff --git a/odb/context.cxx b/odb/context.cxx
index dd4019a..13fc1b3 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -1472,7 +1472,7 @@ utype (semantics::data_member& m,
}
}
- if (s->global_scope ())
+ if (!s->named_p () || s->global_scope ())
break;
}
@@ -1882,8 +1882,13 @@ schema (semantics::scope& s) const
namespace_* ns (dynamic_cast<namespace_*> (ps));
- if (ns == 0)
- continue; // Some other scope.
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
if (ns->extension ())
ns = &ns->original ();
@@ -1920,7 +1925,8 @@ schema (semantics::scope& s) const
n.swap (r);
}
- if (r.fully_qualified () || ns->global_scope ())
+ if (r.fully_qualified () ||
+ ns->global_scope ()) // Note: namespaces always named.
break;
}
@@ -1952,8 +1958,13 @@ table_name_prefix (semantics::scope& s) const
namespace_* ns (dynamic_cast<namespace_*> (ps));
- if (ns == 0)
- continue; // Some other scope.
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
if (ns->extension ())
ns = &ns->original ();
@@ -1964,7 +1975,7 @@ table_name_prefix (semantics::scope& s) const
r = n.uname () + r;
}
- if (ns->global_scope ())
+ if (ns->global_scope ()) // Note: namespaces always named.
break;
}
diff --git a/odb/parser.cxx b/odb/parser.cxx
index d02de78..c026808 100644
--- a/odb/parser.cxx
+++ b/odb/parser.cxx
@@ -176,6 +176,7 @@ private:
unit* unit_;
scope* scope_;
+ vector<scope*> class_scopes_; // Current hierarchy of class-like scopes.
size_t error_;
decl_set decls_;
@@ -263,6 +264,11 @@ emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
if (stub || !COMPLETE_TYPE_P (c))
return *c_node;
+ // Note: "include" the base classes into the class scope (see comment for
+ // self-typedefs in emit_type_decl()).
+ //
+ class_scopes_.push_back (c_node);
+
// Traverse base information.
//
tree bis (TYPE_BINFO (c));
@@ -557,6 +563,8 @@ emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
diagnose_unassoc_pragmas (decls);
scope_ = prev_scope;
+ class_scopes_.pop_back ();
+
return *c_node;
}
@@ -583,6 +591,8 @@ emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
if (stub || !COMPLETE_TYPE_P (u))
return *u_node;
+ class_scopes_.push_back (u_node);
+
// Collect member declarations so that we can traverse them in
// the source code order.
//
@@ -728,6 +738,7 @@ emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
diagnose_unassoc_pragmas (decls);
scope_ = prev_scope;
+ class_scopes_.pop_back ();
return *u_node;
}
@@ -1085,6 +1096,8 @@ emit ()
break;
}
}
+
+ assert (class_scopes_.empty ());
}
// Diagnose any position pragmas that haven't been associated.
@@ -1207,6 +1220,58 @@ emit_type_decl (tree decl)
size_t c (DECL_SOURCE_COLUMN (decl));
type& node (emit_type (t, decl_access (decl), f, l, c));
+
+ // Omit inner self-typedefs (e.g., a class typedefs itself in its own
+ // scope). Such aliases don't buy us anything (in particular, they cannot
+ // be used to form an fq-name) but they do cause scoping cycles if this
+ // name happens to be used to find outer scope (see scope::scope_()).
+ // Note that this means we can now have class template instantiations that
+ // are not named and therefore don't belong to any scope.
+ //
+ // Note that emit_type() might still enter this decl as a hint. It's fuzzy
+ // whether this is harmless or not.
+ //
+ // Note also that using the normal scope hierarchy does not work in more
+ // complex cases where templates cross-self-typedef. So instead we now use
+ // a special-purpose mechanism (class_scopes_). Note for this to work
+ // correctly (hopefully), the class should be "in scope" for its bases.
+ // Here is a representative examples (inspired by code in Eigen):
+ //
+ // template <typename M>
+ // struct PlainObjectBase
+ // {
+ // typedef M Self;
+ // };
+ //
+ // template <typename T, int X, int Y>
+ // struct Matrix: PlainObjectBase<Matrix<T, X, Y>>
+ // {
+ // typedef PlainObjectBase<Matrix> Base;
+ // typedef Matrix Self;
+ // };
+ //
+ // typedef Matrix<double, 3, 1> Vector3d;
+ //
+ // Here we want both Self's (but not Base) to be skipped.
+ //
+ if (scope* s = dynamic_cast<scope*> (&node))
+ {
+ for (auto i (class_scopes_.rbegin ()); i != class_scopes_.rend (); ++i)
+ {
+ if (s == *i)
+ {
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "omitting inner self-typedef " << s << " (" << &node
+ << ") -> " << name << " at " << f << ":" << l << endl;
+ }
+ return 0;
+ }
+ }
+ }
+
typedefs& edge (unit_->new_edge<typedefs> (*scope_, node, name));
// Find our hint.
@@ -1333,6 +1398,8 @@ emit_class_template (tree t, bool stub)
if (stub || !COMPLETE_TYPE_P (c))
return *ct_node;
+ class_scopes_.push_back (ct_node);
+
// Collect member declarations so that we can traverse them in
// the source code order. For now we are only interested in
// nested class template declarations.
@@ -1388,6 +1455,7 @@ emit_class_template (tree t, bool stub)
diagnose_unassoc_pragmas (decls);
scope_ = prev_scope;
+ class_scopes_.pop_back ();
return *ct_node;
}
@@ -1416,6 +1484,8 @@ emit_union_template (tree t, bool stub)
if (stub || !COMPLETE_TYPE_P (u))
return *ut_node;
+ class_scopes_.push_back (ut_node);
+
// Collect member declarations so that we can traverse them in
// the source code order. For now we are only interested in
// nested class template declarations.
@@ -1471,6 +1541,7 @@ emit_union_template (tree t, bool stub)
diagnose_unassoc_pragmas (decls);
scope_ = prev_scope;
+ class_scopes_.pop_back ();
return *ut_node;
}
diff --git a/odb/processor.cxx b/odb/processor.cxx
index 9cda5e6..d48baa7 100644
--- a/odb/processor.cxx
+++ b/odb/processor.cxx
@@ -2194,8 +2194,13 @@ namespace
namespace_* ns (dynamic_cast<namespace_*> (s));
- if (ns == 0)
- continue; // Some other scope.
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
if (ns->extension ())
ns = &ns->original ();
@@ -2207,7 +2212,7 @@ namespace
break;
}
- if (ns->global_scope ())
+ if (ns->global_scope ()) // Note: namespaces always named.
break;
}
@@ -2702,15 +2707,20 @@ namespace
namespace_* ns (dynamic_cast<namespace_*> (s));
- if (ns == 0)
- continue; // Some other scope.
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
if (ns->extension ())
ns = &ns->original ();
if (!ns->count ("pointer"))
{
- if (ns->global_scope ())
+ if (ns->global_scope ()) // Note: namespace always named.
break;
else
continue;
diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx
index fba9b9b..b5793d0 100644
--- a/odb/semantics/elements.cxx
+++ b/odb/semantics/elements.cxx
@@ -56,7 +56,7 @@ namespace semantics
if (p == &s)
return true;
- if (p->global_scope ())
+ if (!p->named_p () || p->global_scope ())
break;
}
@@ -476,7 +476,7 @@ namespace semantics
// Look in the outer scope unless requested not to or if this is
// the global scope.
//
- if ((flags & exclude_outer) == 0 && !global_scope ())
+ if ((flags & exclude_outer) == 0 && named_p () && !global_scope ())
return scope ().lookup (name, ti, flags, hidden);
return 0;