diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 288625bb5ab20..cd5915b468fd0 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -17,6 +17,8 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/Basic/Assertions.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ExprCXX.h" #include "llvm/Support/raw_ostream.h" using namespace swift; @@ -278,6 +280,43 @@ void ModuleNameLookup::lookupInModule( if (decls.size() - initialCount <= 1) return; + // Some functions like `memchr` are defined both in libc and in libc++. + // Importing both would result in ambiguities, but there are some attributes + // that mark the preferred overloads. See _LIBCPP_PREFERRED_OVERLOAD. + llvm::SmallPtrSet declsToRemove; + bool hasPreferredOverload = false; + for (auto decl : decls) + if (const auto *clangDecl = decl->getClangDecl()) { + if (clangDecl->hasAttr()) { + // FIXME: at some point we might want to call into Clang to implement + // the full enable_if semantics including the constant evaluation of the + // conditions. For now, just look for the first enable_if(true, "...") + // and assume all the rest of the enable_ifs evaluate to true. + bool thisDeclHasPreferredOverload = false; + for (auto clangAttr : + clangDecl->specific_attrs()) { + if (auto litExpr = + dyn_cast(clangAttr->getCond())) { + if (litExpr->getValue()) { + thisDeclHasPreferredOverload = hasPreferredOverload = true; + break; + } + } + } + if (!thisDeclHasPreferredOverload) + declsToRemove.insert(decl); + } else + declsToRemove.insert(decl); + } + + if (hasPreferredOverload) { + decls.erase(std::remove_if(decls.begin() + initialCount, decls.end(), + [&](ValueDecl *d) -> bool { + return declsToRemove.contains(d); + }), + decls.end()); + } + // Remove duplicated declarations, which can happen when the same module is // imported with multiple access paths. llvm::SmallPtrSet knownDecls; diff --git a/test/Interop/Cxx/modules/Inputs/ambiguous_a.h b/test/Interop/Cxx/modules/Inputs/ambiguous_a.h new file mode 100644 index 0000000000000..623fe07e1f579 --- /dev/null +++ b/test/Interop/Cxx/modules/Inputs/ambiguous_a.h @@ -0,0 +1,3 @@ +#pragma once + +void f(int a); diff --git a/test/Interop/Cxx/modules/Inputs/ambiguous_b.h b/test/Interop/Cxx/modules/Inputs/ambiguous_b.h new file mode 100644 index 0000000000000..c93b9861d8d3a --- /dev/null +++ b/test/Interop/Cxx/modules/Inputs/ambiguous_b.h @@ -0,0 +1,3 @@ +#pragma once + +void f(int a) __attribute__((enable_if(a > 0, ""))) __attribute__((enable_if(true, ""))); diff --git a/test/Interop/Cxx/modules/Inputs/module.modulemap b/test/Interop/Cxx/modules/Inputs/module.modulemap index 02f06f1f8f09f..1c760fd08e66b 100644 --- a/test/Interop/Cxx/modules/Inputs/module.modulemap +++ b/test/Interop/Cxx/modules/Inputs/module.modulemap @@ -17,3 +17,13 @@ module TopLevelModule { } export * } + +module AmbiguousA { + header "ambiguous_a.h" + requires cplusplus +} + +module AmbiguousB { + header "ambiguous_a.h" + requires cplusplus +} diff --git a/test/Interop/Cxx/modules/preferred-overload.swift b/test/Interop/Cxx/modules/preferred-overload.swift new file mode 100644 index 0000000000000..f594191325433 --- /dev/null +++ b/test/Interop/Cxx/modules/preferred-overload.swift @@ -0,0 +1,8 @@ +// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default + +import AmbiguousA +import AmbiguousB + +func g(_ x: CInt) { + f(x) +} diff --git a/test/Interop/Cxx/objc-correctness/memchr.swift b/test/Interop/Cxx/objc-correctness/memchr.swift new file mode 100644 index 0000000000000..d0618aa574847 --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/memchr.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify -I %S/Inputs %s + +// REQUIRES: objc_interop +// REQUIRES: VENDOR=apple + +import Darwin + +func test_memchr() { + var src = "hello" + var _ = src.withUTF8 { srcBuf in + Darwin.memchr(srcBuf.baseAddress!, 137, src.count) + } +}