From 78fb7c04ec8825dba7f83d49c8d04853479bba81 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 22 Jan 2024 12:23:20 +0200 Subject: Release version 2.5.0-b.25+1 Backport the following commits from 2.5.0-b.26: - Prevent inner self-typedefs from causing scope cycles. - Skip declarations inside anonymous namespaces. - Support integer template parameters in parser::emit_type_name(). - Make sure configuration report variables are set in skeleton mode. - Make GCC plugin directory, g++ executable name customizable. --- build/root.build | 67 +++++++++++++++++++++++++++++------ manifest | 2 +- odb/buildfile | 21 +++-------- odb/context.cxx | 25 +++++++++---- odb/parser.cxx | 88 +++++++++++++++++++++++++++++++++++++++++++--- odb/processor.cxx | 22 ++++++++---- odb/semantics/elements.cxx | 4 +-- 7 files changed, 182 insertions(+), 47 deletions(-) diff --git a/build/root.build b/build/root.build index a07e058..86a02a0 100644 --- a/build/root.build +++ b/build/root.build @@ -1,6 +1,20 @@ # file : build/root.build # license : GNU GPL v3; see accompanying LICENSE file +# This configuration variable can be used to specify the GCC plugin directory +# instead of auto-discovering it with -print-file-name=plugin. Primarily +# useful when dealing with cross-compilation. +# +config [dir_path, config.report.variable=plugin_dir] \ + config.odb.plugin_dir ?= [null] + +# This configuration variable can be used to specify the GCC g++ executable +# name that will be called by the ODB compiler instead of auto-deriving it +# from config.cxx. Primarily useful when dealing with cross-compilation. +# +config [string, config.report.variable=gxx_name] \ + config.odb.gxx_name ?= [null] + config [bool] config.odb.develop ?= false develop = $config.odb.develop @@ -36,22 +50,48 @@ if ($build.mode != 'skeleton') if ($cxx.id != 'gcc') fail 'ODB compiler can only be built with GCC' - # Determine the GCC plugin directory. - # - # If plugin support is disabled, then -print-file-name will print the name - # we have passed (the real plugin directory will always be absolute). + # Determine the GCC plugin directory unless specified explicitly. # + if ($config.odb.plugin_dir != [null]) + plugin_dir = $config.odb.plugin_dir + else + { + # If plugin support is disabled, then -print-file-name will print the name + # we have passed (the real plugin directory will always be absolute). + # + plugin_dir = [dir_path] $process.run($cxx.path -print-file-name=plugin) + + if ("$plugin_dir" == plugin) + fail "$recall($cxx.path) does not support plugins" + } + # It can also include '..' components (e.g., on Windows) so normalize it for # good measure. # - plugin_dir = [dir_path] $process.run($cxx.path -print-file-name=plugin) - - if ("$plugin_dir" == plugin) - fail "$recall($cxx.path) does not support plugins" - plugin_dir = $normalize($plugin_dir) - config [config.report] plugin_dir + # Determine the g++ executable name unless specified explicitly. + # + if ($config.odb.gxx_name != [null]) + gxx_name = $config.odb.gxx_name + else + { + # Unless cross-compiling, pass the C++ compiler's recall path as the g++ + # name. + # + # Note that we used to compare complete target triplets but that prooved + # too strict. For example, we may be running on x86_64-apple-darwin17.7.0 + # while the compiler is targeting x86_64-apple-darwin17.3.0. + # + if ($cxx.target.cpu == $build.host.cpu && \ + $cxx.target.system == $build.host.system) + { + gxx_name = $recall($cxx.path) + } + else + fail "g++ executable name must be specified explicitly with \ +config.odb.gxx_name when cross-compiling" + } # Extract the copyright notice from the LICENSE file. # @@ -61,3 +101,10 @@ if ($build.mode != 'skeleton') 'Copyright \(c\) (.+)\.', \ '\1') } +else +{ + # Set for report. + # + plugin_dir = [null] + gxx_name = [null] +} diff --git a/manifest b/manifest index 13c4091..c5e13ed 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ : 1 name: odb -version: 2.5.0-b.25 +version: 2.5.0-b.25+1 summary: ODB compiler license: GPL-3.0-only topics: C++, ORM, source code generation, object persistence, \ diff --git a/odb/buildfile b/odb/buildfile index 20f8718..34a6329 100644 --- a/odb/buildfile +++ b/odb/buildfile @@ -32,22 +32,6 @@ if ($cxx.target.class != 'windows') # plugin{*}: install = bin/ -# Unless cross-compiling, pass the C++ compiler's recall path as the g++ -# name. At some point we should also make it configurable. -# -# Note that we used to compare complete target triplets but that prooved too -# strict. For example, we may be running on x86_64-apple-darwin17.7.0 while -# the compiler is targeting x86_64-apple-darwin17.3.0. -# -if ($cxx.target.cpu == $build.host.cpu && \ - $cxx.target.system == $build.host.system) -{ - gxx_name = $recall($cxx.path) - gxx_name = $regex.replace($gxx_name, '\\', '\\\\') # Escape back slashes. -} -else - gxx_name = g++ - import libs = libcutl%lib{cutl} import libs += libstudxml%lib{studxml} @@ -98,7 +82,10 @@ libus{odb}: {hxx ixx txx cxx}{** -odb -options -pregenerated/**} $libs # Build options. # -cxx.poptions += "-I$plugin_dir/include" "-DODB_GXX_NAME=\"$gxx_name\"" +# Note: escape backslashes in gxx_name. +# +cxx.poptions += "-I$plugin_dir/include" +cxx.poptions += "-DODB_GXX_NAME=\"$regex.replace($gxx_name, '\\', '\\\\')\"" cxx.poptions += -DODB_BUILD2 # @@ TMP while supporting other build systems. ## Consumption build ($develop == false). 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 (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 (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 58388c9..c026808 100644 --- a/odb/parser.cxx +++ b/odb/parser.cxx @@ -176,6 +176,7 @@ private: unit* unit_; scope* scope_; + vector 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; } @@ -942,9 +953,10 @@ collect (tree ns) if (!DECL_IS_BUILTIN (decl) || DECL_NAMESPACE_STD_P (decl)) { + tree dn (DECL_NAME (decl)); + if (trace) { - tree dn (DECL_NAME (decl)); char const* name (dn ? IDENTIFIER_POINTER (dn) : ""); ts << "namespace " << name << " at " @@ -952,7 +964,12 @@ collect (tree ns) << DECL_SOURCE_LINE (decl) << endl; } - collect (decl); + // Skip anonymous namespaces (there could be nothing of interest to us + // inside but they wreck havoc with our attempts to sort declarations + // into namespaces). + // + if (dn != 0) + collect (decl); } } } @@ -1079,6 +1096,8 @@ emit () break; } } + + assert (class_scopes_.empty ()); } // Diagnose any position pragmas that haven't been associated. @@ -1201,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 + // struct PlainObjectBase + // { + // typedef M Self; + // }; + // + // template + // struct Matrix: PlainObjectBase> + // { + // typedef PlainObjectBase Base; + // typedef Matrix Self; + // }; + // + // typedef Matrix Vector3d; + // + // Here we want both Self's (but not Base) to be skipped. + // + if (scope* s = dynamic_cast (&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 (*scope_, node, name)); // Find our hint. @@ -1327,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. @@ -1382,6 +1455,7 @@ emit_class_template (tree t, bool stub) diagnose_unassoc_pragmas (decls); scope_ = prev_scope; + class_scopes_.pop_back (); return *ct_node; } @@ -1410,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. @@ -1465,6 +1541,7 @@ emit_union_template (tree t, bool stub) diagnose_unassoc_pragmas (decls); scope_ = prev_scope; + class_scopes_.pop_back (); return *ut_node; } @@ -2053,9 +2130,12 @@ emit_type_name (tree type, bool direct) if (i != 0) id += ", "; - // Assume type-only arguments. + // Assume integer and type-only arguments. // - id += emit_type_name (a); + if (TREE_CODE (a) == INTEGER_CST) + id += to_string (integer_value (a)); + else + id += emit_type_name (a); } id += '>'; 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 (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 (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; -- cgit v1.1