[libcxx-commits] [libcxx] [libc++][modules] Adds std.compat module. (PR #71438)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Nov 21 09:57:51 PST 2023


================
@@ -199,50 +223,121 @@ static bool is_viable_declaration(const clang::NamedDecl* decl) {
 /// Returns the name is a reserved name.
 ///
 /// Detected reserved names are names starting with __ or _[A-Z].
-/// These names can be in the namespace std or any namespace inside std. For
-/// example std::ranges contains reserved names to implement the Niebloids.
+/// These names can be in the global namespace, std namespace or any namespace
+/// inside std. For example, std::ranges contains reserved names to implement
+/// the Niebloids.
 ///
-/// This test misses 2 candidates which are not used in libc++
+/// This test misses candidates which are not used in libc++
 /// * any identifier with two underscores not at the start
-/// * a name with a leading underscore in the global namespace
-bool is_reserved_name(const std::string& name) {
+bool is_reserved_name(std::string_view name) {
+  if (name.starts_with("_"))
+    return name.size() > 1 && (name[1] == '_' || std::isupper(name[1]));
+
   std::size_t pos = name.find("::_");
   if (pos == std::string::npos)
     return false;
 
   if (pos + 3 > name.size())
     return false;
 
+  // This is a public name declared in cstdlib.
+  if (name == "std::_Exit")
+    return false;
+
   return name[pos + 3] == '_' || std::isupper(name[pos + 3]);
 }
 
+/// Some declarations in the global namespace are exported from the std module.
+static bool is_valid_global_name(std::string_view name) {
+  static const std::set<std::string_view> valid{
+      "operator delete", "operator delete[]", "operator new", "operator new[]"};
+  return valid.contains(name);
+}
+
+static bool is_valid_declaration_context(
+    const clang::NamedDecl& decl, std::string_view name, header_exportable_declarations::FileType file_type) {
+  if (decl.getDeclContext()->isNamespace())
+    return true;
+
+  if (is_valid_global_name(name))
+    return true;
+
+  return file_type != header_exportable_declarations::FileType::Header;
+}
+
+static bool is_module(header_exportable_declarations::FileType file_type) {
+  switch (file_type) {
+  case header_exportable_declarations::FileType::Module:
+  case header_exportable_declarations::FileType::ModulePartition:
+  case header_exportable_declarations::FileType::CompatModule:
+  case header_exportable_declarations::FileType::CompatModulePartition:
+    return true;
+
+  case header_exportable_declarations::FileType::Header:
+  case header_exportable_declarations::FileType::CHeader:
+    return false;
+
+  case header_exportable_declarations::FileType::Unknown:
+    llvm::errs() << "This should be unreachable.\n";
+    break;
+  }
+}
+
 void header_exportable_declarations::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
   if (const auto* decl = result.Nodes.getNodeAs<clang::NamedDecl>("header_exportable_declarations"); decl != nullptr) {
     if (!is_viable_declaration(decl))
       return;
 
     std::string name = get_qualified_name(*decl);
+    if (name.empty())
+      return;
+
     if (is_reserved_name(name))
       return;
 
     // For modules only take the declarations exported.
-    if (file_type_ == FileType::ModulePartition || file_type_ == FileType::Module)
+    if (is_module(file_type_))
       if (decl->getModuleOwnershipKind() != clang::Decl::ModuleOwnershipKind::VisibleWhenImported)
         return;
 
+    if (!is_valid_declaration_context(*decl, name, file_type_))
+      return;
+
     if (decls_.contains(name)) {
       // For modules avoid exporting the same named declaration twice. For
       // header files this is common and valid.
-      if (file_type_ == FileType::ModulePartition)
+      if (file_type_ == FileType::ModulePartition || file_type_ == FileType::CompatModulePartition)
         // After the warning the script continues.
         // The test will fail since modules have duplicated entries and headers not.
         llvm::errs() << "Duplicated export of '" << name << "'.\n";
       else
         return;
     }
 
-    std::cout << "using " << std::string{name} << ";\n";
+    // For named declarations in std this is valid
+    //   using std::foo;
+    // for named declarations it is invalid to use
+    //   using bar;
+    // Since fully qualifying named declarations in the std namespace is valid
+    // using fully qualified names unconditionally.
+    std::cout << "using ::" << std::string{name} << ";\n";
     decls_.insert(name);
+  } else if (const auto* decl = result.Nodes.getNodeAs<clang::NamedDecl>("cheader_exportable_declarations");
+             decl != nullptr) {
+    if (decl->getDeclContext()->isNamespace())
+      return;
+
+    if (!is_viable_declaration(decl))
+      return;
+
+    std::string name = get_qualified_name(*decl);
+    if (is_reserved_name(name))
----------------
ldionne wrote:

Do we see the declaration of `::_Exit` here? If so do we filter reserved names correctly? I think the result is that right now we're not generating a test for std.compat's definition of `::_Exit`.

https://github.com/llvm/llvm-project/pull/71438


More information about the libcxx-commits mailing list