Files
swift-mirror/test/Interop/Cxx/modules/prevent-module-shadowed-by-namespace.swift
John Hui 76a1742aca [cxx-interop] Use formal C++ interop mode to fix name lookup in module interfaces (#79984)
It is possible for a module interface (e.g., ModuleA) to be generated
with C++ interop disabled, and then rebuilt with C++ interop enabled
(e.g., because ModuleB, which imports ModuleA, has C++ interop enabled).

This circumstance can lead to various issues when name lookup behaves
differently depending on whether C++ interop is enabled, e.g., when
a module name is shadowed by a namespace of the same name---this only
happens in C++ because namespaces do not exist in C. Unfortunately,
naming namespaces the same as a module is a common C++ convention,
leading to many textual interfaces whose fully-qualified identifiers
(e.g., c_module.c_member) cannot be correctly resolved when C++ interop
is enabled (because c_module is shadowed by a namespace of the same
name).

This patch does two things. First, it introduces a new frontend flag,
-formal-cxx-interoperability-mode, which records the C++ interop mode
a module interface was originally compiled with. Doing so allows
subsequent consumers of that interface to interpret it according to the
formal C++ interop mode. Note that the actual "versioning" used by this
flag is very crude: "off" means disabled, and "swift-6" means enabled.
This is done to be compatible with C++ interop compat versioning scheme,
which seems to produce some invalid (but unused) version numbers. The
versioning scheme for both the formal and actual C++ interop modes
should be clarified and fixed in a subsequent patch.

The second thing this patch does is fix the module/namespace collision
issue in module interface files. It uses the formal C++ interop mode to
determine whether it should resolve C++-only decls during name lookup.
For now, the fix is very minimal and conservative: it only filters out
C++ namespaces during unqualified name lookup in an interface that was
originally generated without C++ interop. Doing so should fix the issue
while minimizing the chance for collateral breakge. More cases other
than C++ namespaces should be added in subsequent patches, with
sufficient testing and careful consideration.

rdar://144566922
2025-03-13 23:24:18 -07:00

58 lines
2.4 KiB
Swift

// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/include)
// RUN: split-file %s %t
// Generate textual interface for shim.swift without C++ interop enabled, then
// re-compile it with C++ interop enabled (because it is used by program.swift,
// which is compiled with C++ interop enabled).
//
// RUN: %empty-directory(%t/lib)
// RUN: %target-swift-emit-module-interface(%t/lib/shim.swiftinterface) %t/shim.swift -module-name shim -I %t/include
// RUN: %FileCheck %t/shim.swift < %t/lib/shim.swiftinterface
// RUN: %target-swift-frontend %t/program.swift -typecheck -verify -cxx-interoperability-mode=default -I %t/include -I %t/lib
//--- include/module.modulemap
// A Clang module which will first be compiled in C mode, and then later compiled in C++ mode
module c2cxx {
header "c2cxx.h"
export *
}
//--- include/c2cxx.h
// A header file that defines a namespace with the same name as the module,
// a common C++ idiom. We want to make sure that it does not shadow the module
// in textual interfaces generated with C++ interop disabled, but later compiled
// with C++ interop enabled.
#ifndef __C2CXX_NAMESPACE_H
#define __C2CXX_NAMESPACE_H
typedef int c2cxx_number; // always available and resilient
#ifdef __cplusplus
namespace c2cxx { typedef c2cxx_number number; }; // only available in C++
#endif // __cplusplus
#endif // __C2CXX_NAMESPACE_H
//--- shim.swift
// A shim around c2cxx that exposes a c2cxx decl in its module interface
import c2cxx
// Exposes a (fully-qualified) c2cxx decl in its module interface.
public func shimId(_ n: c2cxx_number) -> c2cxx_number { return n }
// CHECK: public func shimId(_ n: c2cxx.c2cxx_number) -> c2cxx.c2cxx_number
// ^^^^^`- refers to the module
// @inlinable functions have their bodies splatted in the module interface file;
// those verbatim bodies may contain unqualified names.
@inlinable public func shimIdInline(_ n: c2cxx_number) -> c2cxx_number {
// CHECK: public func shimIdInline(_ n: c2cxx.c2cxx_number) -> c2cxx.c2cxx_number
// ^^^^^`- refers to the module
let m: c2cxx_number = n
// CHECK: let m: c2cxx_number = n
// ^^^^^^^^^^^^`- verbatim (unqualified)
return m
}
//--- program.swift
// Uses the shim and causes it to be (re)built from its interface
import shim
func numberwang() { let _ = shimId(42) + shimIdInline(24) }