From ffa6d65f1249d597b016cc0144004a3b5c7ec5c5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 10:59:01 -0700 Subject: [PATCH 01/15] [Options] Make `-import-bridging-header` the canonical spelling instead of `-import-objc-header` This command-line option hasn't been Objective-C specific ever, really. Make the language-independent spelling the primary one to make that more obvious. --- include/swift/Option/Options.td | 8 ++-- lib/Driver/Driver.cpp | 4 +- lib/Driver/ToolChains.cpp | 12 +++--- .../ArgsToFrontendOptionsConverter.cpp | 2 +- lib/Frontend/CompilerInvocation.cpp | 2 +- test/Driver/bridging-pch.swift | 42 +++++++++---------- test/Driver/working-directory.swift | 6 +-- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index c3d87e4941df9..9f9be9bbcb6db 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -347,12 +347,12 @@ def import_underlying_module : Flag<["-"], "import-underlying-module">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Implicitly imports the Objective-C half of a module">; -def import_objc_header : Separate<["-"], "import-objc-header">, - Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, - HelpText<"Implicitly imports an Objective-C header file">; def import_bridging_header : Separate<["-"], "import-bridging-header">, + Flags<[FrontendOption, ArgumentIsPath]>, + HelpText<"Implicitly imports a C header file">; +def import_objc_header : Separate<["-"], "import-objc-header">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, - Alias; + Alias; def import_pch : Separate<["-"], "import-pch">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, HelpText<"Import bridging header PCH file">; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 6c42ca0c8a64c..6f77e2cd84982 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -144,7 +144,7 @@ static void validateLegacyUnsupportedArgs(DiagnosticEngine &diags, static void validateBridgingHeaderArgs(DiagnosticEngine &diags, const ArgList &args) { - if (!args.hasArgNoClaim(options::OPT_import_objc_header)) + if (!args.hasArgNoClaim(options::OPT_import_bridging_header)) return; if (args.hasArgNoClaim(options::OPT_import_underlying_module)) @@ -1521,7 +1521,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (Args.hasFlag(options::OPT_enable_bridging_pch, options::OPT_disable_bridging_pch, true)) { - if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) { + if (Arg *A = Args.getLastArg(options::OPT_import_bridging_header)) { StringRef Value = A->getValue(); auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); if (Ty == file_types::TY_ClangHeader) { diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 8011dd7d02b62..e36ae166e755e 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -524,7 +524,7 @@ ToolChain::constructInvocation(const CompileJobAction &job, // Pass along an -import-objc-header arg, replacing the argument with the name // of any input PCH to the current action if one is present. - if (context.Args.hasArgNoClaim(options::OPT_import_objc_header)) { + if (context.Args.hasArgNoClaim(options::OPT_import_bridging_header)) { bool ForwardAsIs = true; bool bridgingPCHIsEnabled = context.Args.hasFlag(options::OPT_enable_bridging_pch, @@ -543,7 +543,7 @@ ToolChain::constructInvocation(const CompileJobAction &job, } } if (ForwardAsIs) { - context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); } if (usePersistentPCH) { context.Args.AddLastArg(Arguments, options::OPT_pch_output_dir); @@ -972,7 +972,7 @@ ToolChain::constructInvocation(const InterpretJobAction &job, addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments); addRuntimeLibraryFlags(context.OI, Arguments); - context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); context.Args.AddLastArg(Arguments, options::OPT_parse_sil); @@ -1233,7 +1233,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, options::OPT_omit_extension_block_symbols); context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_minimum_access_level); - context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); @@ -1276,7 +1276,7 @@ ToolChain::constructInvocation(const VerifyModuleInterfaceJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); @@ -1342,7 +1342,7 @@ ToolChain::constructInvocation(const REPLJobAction &job, addCommonFrontendArgs(context.OI, context.Output, context.Args, FrontendArgs); addRuntimeLibraryFlags(context.OI, FrontendArgs); - context.Args.AddLastArg(FrontendArgs, options::OPT_import_objc_header); + context.Args.AddLastArg(FrontendArgs, options::OPT_import_bridging_header); context.Args.addAllArgs(FrontendArgs, {options::OPT_framework, options::OPT_L}); ToolChain::addLinkedLibArgs(context.Args, FrontendArgs); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index ecbcfbd3d44d1..ce51123b5700a 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -913,7 +913,7 @@ static inline bool isPCHFilenameExtension(StringRef path) { void ArgsToFrontendOptionsConverter::computeImportObjCHeaderOptions() { using namespace options; - if (const Arg *A = Args.getLastArgNoClaim(OPT_import_objc_header)) { + if (const Arg *A = Args.getLastArgNoClaim(OPT_import_bridging_header)) { // Legacy support for passing PCH file through `-import-objc-header`. if (isPCHFilenameExtension(A->getValue())) Opts.ImplicitObjCPCHPath = A->getValue(); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d31373ff4b4d7..57339370b3573 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2109,7 +2109,7 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args, else if (Args.hasArg(OPT_emit_pcm) || Args.hasArg(OPT_dump_pcm)) Opts.Mode = ClangImporterOptions::Modes::PrecompiledModule; - if (auto *A = Args.getLastArg(OPT_import_objc_header)) + if (auto *A = Args.getLastArg(OPT_import_bridging_header)) Opts.BridgingHeader = A->getValue(); if (auto *A = Args.getLastArg(OPT_import_pch)) Opts.BridgingHeaderPCH = A->getValue(); diff --git a/test/Driver/bridging-pch.swift b/test/Driver/bridging-pch.swift index 522303fe7c82d..2f50b15d3a36d 100644 --- a/test/Driver/bridging-pch.swift +++ b/test/Driver/bridging-pch.swift @@ -6,63 +6,63 @@ // YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // YESPCHACT: 3: compile, {2, 1}, none -// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHACT +// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-actions -import-bridging-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHACT // NOPCHACT: 0: input, "{{.*}}bridging-pch.swift", swift // NOPCHACT: 1: compile, {0}, none -// RUN: %target-build-swift -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHJOB +// RUN: %target-build-swift -typecheck -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHJOB // YESPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -emit-pch -o {{.*}}bridging-header-{{.*}}.pch -// YESPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -import-objc-header {{.*}}bridging-header-{{.*}}.pch +// YESPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -import-bridging-header {{.*}}bridging-header-{{.*}}.pch -// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHJOB -// NOPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -import-objc-header {{.*}}Inputs/bridging-header.h +// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHJOB +// NOPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -import-bridging-header {{.*}}Inputs/bridging-header.h -// RUN: %target-build-swift -typecheck -driver-print-jobs -index-store-path %t/idx -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=INDEXSTORE +// RUN: %target-build-swift -typecheck -driver-print-jobs -index-store-path %t/idx -import-bridging-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=INDEXSTORE // INDEXSTORE: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -index-store-path {{.*}}/idx{{"?}} -emit-pch -o {{.*}}bridging-header-{{.*}}.pch // RUN: echo "{\"\": {\"swift-dependencies\": \"%/t/master.swiftdeps\"}, \"%/s\": {\"swift-dependencies\": \"%/t/bridging-header.swiftdeps\"}}" > %t.json -// RUN: %target-build-swift -typecheck -incremental -enable-bridging-pch -output-file-map %t.json -import-objc-header %S/Inputs/bridging-header.h %s +// RUN: %target-build-swift -typecheck -incremental -enable-bridging-pch -output-file-map %t.json -import-bridging-header %S/Inputs/bridging-header.h %s // RUN: mkdir %t/tmp -// RUN: env TMP="%t/tmp/" TMPDIR="%t/tmp/" not %target-build-swift -typecheck -import-objc-header %S/../Inputs/empty.h -driver-use-frontend-path "%{python.unquoted};%S/Inputs/crash-after-generating-pch.py" -v %s +// RUN: env TMP="%t/tmp/" TMPDIR="%t/tmp/" not %target-build-swift -typecheck -import-bridging-header %S/../Inputs/empty.h -driver-use-frontend-path "%{python.unquoted};%S/Inputs/crash-after-generating-pch.py" -v %s // RUN: ls %/t/tmp/ | grep .*pch // Test persistent PCH -// RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACT +// RUN: %target-build-swift -typecheck -driver-print-actions -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACT // PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // PERSISTENT-YESPCHACT: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACT: 3: compile, {2, 1}, none -// RUN: %target-build-swift -c -driver-print-actions -embed-bitcode -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACTBC +// RUN: %target-build-swift -c -driver-print-actions -embed-bitcode -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACTBC // PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // PERSISTENT-YESPCHACTBC: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACTBC: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACTBC: 3: compile, {2, 1}, llvm-bc -// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=NOPCHACT +// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-actions -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=NOPCHACT -// RUN: %target-build-swift -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -disable-bridging-pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-DISABLED-YESPCHJOB -// RUN: %target-build-swift -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -whole-module-optimization -disable-bridging-pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-DISABLED-YESPCHJOB +// RUN: %target-build-swift -typecheck -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -disable-bridging-pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-DISABLED-YESPCHJOB +// RUN: %target-build-swift -typecheck -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -whole-module-optimization -disable-bridging-pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-DISABLED-YESPCHJOB // PERSISTENT-DISABLED-YESPCHJOB-NOT: -pch-output-dir -// RUN: %target-build-swift -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -serialize-diagnostics %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHJOB-DIAG1 +// RUN: %target-build-swift -typecheck -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -serialize-diagnostics %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHJOB-DIAG1 // PERSISTENT-YESPCHJOB-DIAG1: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -serialize-diagnostics-path {{.*}}bridging-header-{{.*}}.dia{{"?}} {{.*}} -emit-pch -pch-output-dir {{.*}}/pch -// RUN: %target-build-swift -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch-out-dir -serialize-diagnostics %s -emit-module -emit-module-path /module-path-dir 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHJOB-DIAG2 +// RUN: %target-build-swift -typecheck -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch-out-dir -serialize-diagnostics %s -emit-module -emit-module-path /module-path-dir 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHJOB-DIAG2 // PERSISTENT-YESPCHJOB-DIAG2: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -serialize-diagnostics-path {{.*}}/pch-out-dir{{/|\\\\}}bridging-header-{{.*}}.dia{{"?}} {{.*}} -emit-pch -pch-output-dir {{.*}}/pch-out-dir -// RUN: %target-build-swift -typecheck -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -parseable-output -driver-skip-execution %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-OUTPUT +// RUN: %target-build-swift -typecheck -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -parseable-output -driver-skip-execution %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-OUTPUT // PERSISTENT-OUTPUT-NOT: "outputs": [ -// RUN: %target-build-swift -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -whole-module-optimization %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-WMO-YESPCHJOB --implicit-check-not pch-disable-validation -// PERSISTENT-WMO-YESPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -import-objc-header {{.*}}bridging-header.h{{"?}} -pch-output-dir {{.*}}/pch +// RUN: %target-build-swift -typecheck -driver-print-jobs -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch -whole-module-optimization %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-WMO-YESPCHJOB --implicit-check-not pch-disable-validation +// PERSISTENT-WMO-YESPCHJOB: {{.*}}swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend {{.*}} -import-bridging-header {{.*}}bridging-header.h{{"?}} -pch-output-dir {{.*}}/pch -// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-jobs -import-objc-header %/S/Inputs/bridging-header.h -pch-output-dir %t/pch %/s 2>&1 | %FileCheck %s -check-prefix=NOPCHJOB -// RUN: %target-build-swift -typecheck -incremental -enable-bridging-pch -output-file-map %t.json -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s +// RUN: %target-build-swift -typecheck -disable-bridging-pch -driver-print-jobs -import-bridging-header %/S/Inputs/bridging-header.h -pch-output-dir %t/pch %/s 2>&1 | %FileCheck %s -check-prefix=NOPCHJOB +// RUN: %target-build-swift -typecheck -incremental -enable-bridging-pch -output-file-map %t.json -import-bridging-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s -// RUN: %target-build-swift -### -typecheck -O -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=OPTPCH +// RUN: %target-build-swift -### -typecheck -O -import-bridging-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=OPTPCH // OPTPCH: swift{{(c|c-legacy-driver|-frontend)?(\.exe)?"?}} -frontend // OPTPCH-SAME: -O{{ }} // OPTPCH-SAME: -emit-pch diff --git a/test/Driver/working-directory.swift b/test/Driver/working-directory.swift index 26919e89d6472..b5ab72fab7f65 100644 --- a/test/Driver/working-directory.swift +++ b/test/Driver/working-directory.swift @@ -32,10 +32,10 @@ // RUN: cd %t && %swiftc_driver -driver-print-jobs -working-directory %/S/Inputs -emit-executable %/s -L=. | %FileCheck %s -check-prefix=L_PATH // L_PATH: -L {{"?}}SOURCE_DIR/test/Driver/Inputs -// RUN: cd %t && %swiftc_driver -driver-print-jobs -working-directory %/S/Inputs -c %/s -disable-bridging-pch -import-objc-header bridging-header.h | %FileCheck %s -check-prefix=OBJC_HEADER1 -// OBJC_HEADER1: -import-objc-header {{"?}}SOURCE_DIR/test/Driver/Inputs{{/|\\\\}}bridging-header.h +// RUN: cd %t && %swiftc_driver -driver-print-jobs -working-directory %/S/Inputs -c %/s -disable-bridging-pch -import-bridging-header bridging-header.h | %FileCheck %s -check-prefix=OBJC_HEADER1 +// OBJC_HEADER1: -import-bridging-header {{"?}}SOURCE_DIR/test/Driver/Inputs{{/|\\\\}}bridging-header.h -// RUN: cd %t && %swiftc_driver -driver-print-jobs -working-directory %/S/Inputs -c %/s -enable-bridging-pch -import-objc-header bridging-header.h | %FileCheck %s -check-prefix=OBJC_HEADER2 +// RUN: cd %t && %swiftc_driver -driver-print-jobs -working-directory %/S/Inputs -c %/s -enable-bridging-pch -import-bridging-header bridging-header.h | %FileCheck %s -check-prefix=OBJC_HEADER2 // OBJC_HEADER2: SOURCE_DIR/test/Driver/Inputs{{/|\\\\}}bridging-header.h{{"? .*}}-emit-pch // RUN: cd %t && %swiftc_driver -driver-print-jobs -working-directory %/S/Inputs -c %/s -o main.o | %FileCheck %s -check-prefix=OUTPUT_OBJ From 2383d7ab2dec10bedd7c6a2db0401b3168f43fce Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 11:58:47 -0700 Subject: [PATCH 02/15] Introduce "-internal" variant of bridging header import flags The flags "-import-bridging-header" and "-import-pch" import a bridging header, treating the contents as a public import. Introduce "internal-" variants of both flags that provide the same semantics, but are intended to treat the imported contents as if they came in through an internal import. This is just plumbing of the options for the moment. --- include/swift/AST/DiagnosticsFrontend.def | 2 + include/swift/Basic/LangOptions.h | 4 ++ include/swift/Frontend/FrontendOptions.h | 8 ++- include/swift/Option/Options.td | 9 ++++ lib/ClangImporter/ClangImporter.cpp | 1 + lib/ClangImporter/ImporterImpl.h | 1 + lib/Driver/Driver.cpp | 7 ++- lib/Driver/ToolChains.cpp | 32 ++++++++---- .../ArgsToFrontendOptionsConverter.cpp | 30 +++++++++-- lib/Frontend/CompilerInvocation.cpp | 20 +++++++- test/ClangImporter/Inputs/c-bridging-header.h | 3 ++ .../InternalBridgingHeader/pch.swift | 50 +++++++++++++++++++ 12 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 test/ClangImporter/Inputs/c-bridging-header.h create mode 100644 test/ClangImporter/InternalBridgingHeader/pch.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 065291fe82fb4..754ea7d3780d4 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -636,6 +636,8 @@ NOTE(dependency_scan_unexpected_variant_module_map_note, none, NOTE(dependency_scan_unexpected_variant_extra_arg_note, none, "%select{first|second}0 module command-line has extra argument: '%1'", (bool, StringRef)) +ERROR(bridging_header_and_pch_internal_mismatch,none, + "bridging header and precompiled header options mismatch on internal vs. public import", ()) #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index cfd593e18112f..e3991483dde0e 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -1046,6 +1046,10 @@ namespace swift { /// The bridging header PCH file. std::string BridgingHeaderPCH; + /// Whether the bridging header and PCH file are considered to be + /// internal imports. + bool BridgingHeaderIsInternal = false; + /// When automatically generating a precompiled header from the bridging /// header, place it in this directory. std::string PrecompiledHeaderOutputDir; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index eee71255c1392..38b91a728e8bb 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -66,12 +66,16 @@ class FrontendOptions { bool isOutputFileDirectory() const; - /// An Objective-C header to import and make implicitly visible. + /// A C header to import and make implicitly visible. std::string ImplicitObjCHeaderPath; - /// An Objective-C pch to import and make implicitly visible. + /// A C pch to import and make implicitly visible. std::string ImplicitObjCPCHPath; + /// Whether the imported C header or precompiled header is considered + /// an internal import (vs. the default, a public import). + bool ImportHeaderAsInternal = false; + /// The map of aliases and real names of imported or referenced modules. llvm::StringMap ModuleAliasMap; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 9f9be9bbcb6db..9efd7fd8eb90b 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -353,10 +353,19 @@ def import_bridging_header : Separate<["-"], "import-bridging-header">, def import_objc_header : Separate<["-"], "import-objc-header">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, Alias; + +def internal_import_bridging_header : Separate<["-"], "internal-import-bridging-header">, + Flags<[FrontendOption, ArgumentIsPath]>, + HelpText<"Imports an C header file as an internal import">; + def import_pch : Separate<["-"], "import-pch">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, HelpText<"Import bridging header PCH file">; +def internal_import_pch : Separate<["-"], "internal-import-pch">, + Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, + HelpText<"Import bridging header PCH file as internal">; + def pch_output_dir: Separate<["-"], "pch-output-dir">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, HelpText<"Directory to persist automatically created precompiled bridging headers">; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 6182b43e83a50..c01395fe30859 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -2714,6 +2714,7 @@ ClangImporter::Implementation::Implementation( DisableSwiftBridgeAttr(ctx.ClangImporterOpts.DisableSwiftBridgeAttr), BridgingHeaderExplicitlyRequested( !ctx.ClangImporterOpts.BridgingHeader.empty()), + BridgingHeaderIsInternal(ctx.ClangImporterOpts.BridgingHeaderIsInternal), DisableOverlayModules(ctx.ClangImporterOpts.DisableOverlayModules), EnableClangSPI(ctx.ClangImporterOpts.EnableClangSPI), IsReadingBridgingPCH(false), diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 3138cca331a52..4b7126a7b2d0d 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -481,6 +481,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation const bool ImportForwardDeclarations; const bool DisableSwiftBridgeAttr; const bool BridgingHeaderExplicitlyRequested; + const bool BridgingHeaderIsInternal; const bool DisableOverlayModules; const bool EnableClangSPI; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 6f77e2cd84982..60903dd63226d 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -144,7 +144,8 @@ static void validateLegacyUnsupportedArgs(DiagnosticEngine &diags, static void validateBridgingHeaderArgs(DiagnosticEngine &diags, const ArgList &args) { - if (!args.hasArgNoClaim(options::OPT_import_bridging_header)) + if (!args.hasArgNoClaim(options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header)) return; if (args.hasArgNoClaim(options::OPT_import_underlying_module)) @@ -1521,7 +1522,9 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (Args.hasFlag(options::OPT_enable_bridging_pch, options::OPT_disable_bridging_pch, true)) { - if (Arg *A = Args.getLastArg(options::OPT_import_bridging_header)) { + if (Arg *A = Args.getLastArg( + options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header)) { StringRef Value = A->getValue(); auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); if (Ty == file_types::TY_ClangHeader) { diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index e36ae166e755e..2d2deec00e082 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -522,19 +522,28 @@ ToolChain::constructInvocation(const CompileJobAction &job, addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments); addRuntimeLibraryFlags(context.OI, Arguments); - // Pass along an -import-objc-header arg, replacing the argument with the name - // of any input PCH to the current action if one is present. - if (context.Args.hasArgNoClaim(options::OPT_import_bridging_header)) { + // Pass along an -(internal-)?import-bridging-header arg, replacing the + // argument with the name of any input PCH to the current action if one is + // present. + if (context.Args.hasArgNoClaim(options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header)) { bool ForwardAsIs = true; bool bridgingPCHIsEnabled = context.Args.hasFlag(options::OPT_enable_bridging_pch, options::OPT_disable_bridging_pch, true); bool usePersistentPCH = bridgingPCHIsEnabled && context.Args.hasArg(options::OPT_pch_output_dir); + bool isInternalImport = context.Args.getLastArgNoClaim( + options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header) + ->getOption().getID() == options::OPT_internal_import_bridging_header; if (!usePersistentPCH) { for (auto *IJ : context.Inputs) { if (!IJ->getOutput().getAnyOutputForType(file_types::TY_PCH).empty()) { - Arguments.push_back("-import-objc-header"); + if (isInternalImport) + Arguments.push_back("-internal-import-bridging-header"); + else + Arguments.push_back("-import-bridging-header"); addInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_PCH); ForwardAsIs = false; @@ -543,7 +552,8 @@ ToolChain::constructInvocation(const CompileJobAction &job, } } if (ForwardAsIs) { - context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header); } if (usePersistentPCH) { context.Args.AddLastArg(Arguments, options::OPT_pch_output_dir); @@ -972,7 +982,8 @@ ToolChain::constructInvocation(const InterpretJobAction &job, addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments); addRuntimeLibraryFlags(context.OI, Arguments); - context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header); context.Args.AddLastArg(Arguments, options::OPT_parse_sil); @@ -1233,7 +1244,8 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, options::OPT_omit_extension_block_symbols); context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_minimum_access_level); - context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header); Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); @@ -1276,7 +1288,8 @@ ToolChain::constructInvocation(const VerifyModuleInterfaceJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header); + context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header); Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); @@ -1342,7 +1355,8 @@ ToolChain::constructInvocation(const REPLJobAction &job, addCommonFrontendArgs(context.OI, context.Output, context.Args, FrontendArgs); addRuntimeLibraryFlags(context.OI, FrontendArgs); - context.Args.AddLastArg(FrontendArgs, options::OPT_import_bridging_header); + context.Args.AddLastArg(FrontendArgs, options::OPT_import_bridging_header, + options::OPT_internal_import_bridging_header); context.Args.addAllArgs(FrontendArgs, {options::OPT_framework, options::OPT_L}); ToolChain::addLinkedLibArgs(context.Args, FrontendArgs); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index ce51123b5700a..b40573686b280 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -913,18 +913,40 @@ static inline bool isPCHFilenameExtension(StringRef path) { void ArgsToFrontendOptionsConverter::computeImportObjCHeaderOptions() { using namespace options; - if (const Arg *A = Args.getLastArgNoClaim(OPT_import_bridging_header)) { - // Legacy support for passing PCH file through `-import-objc-header`. + bool hadNormalBridgingHeader = false; + if (const Arg *A = Args.getLastArgNoClaim( + OPT_import_bridging_header, + OPT_internal_import_bridging_header)) { + // Legacy support for passing PCH file through `-import-bridging-header`. if (isPCHFilenameExtension(A->getValue())) Opts.ImplicitObjCPCHPath = A->getValue(); else Opts.ImplicitObjCHeaderPath = A->getValue(); - // If `-import-object-header` is used, it means the module has a direct + // If `-import-bridging-header` is used, it means the module has a direct // bridging header dependency and it can be serialized into binary module. Opts.ModuleHasBridgingHeader |= true; + + Opts.ImportHeaderAsInternal = + A->getOption().getID() == OPT_internal_import_bridging_header; + + hadNormalBridgingHeader = true; } - if (const Arg *A = Args.getLastArgNoClaim(OPT_import_pch)) + if (const Arg *A = Args.getLastArgNoClaim(OPT_import_pch, + OPT_internal_import_pch)) { Opts.ImplicitObjCPCHPath = A->getValue(); + + bool importAsInternal = A->getOption().getID() == OPT_internal_import_pch; + + /// Don't let the bridging-header and precompiled-header options differ in + /// whether they are treated as internal or public imports. + if (hadNormalBridgingHeader && + importAsInternal != Opts.ImportHeaderAsInternal) { + Diags.diagnose(SourceLoc(), + diag::bridging_header_and_pch_internal_mismatch); + } + + Opts.ImportHeaderAsInternal = importAsInternal; + } } void ArgsToFrontendOptionsConverter:: computeImplicitImportModuleNames(OptSpecifier id, bool isTestable) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 57339370b3573..697536b1a93dc 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -363,6 +363,8 @@ setBridgingHeaderFromFrontendOptions(ClangImporterOptions &ImporterOpts, if (!FrontendOpts.InputsAndOutputs.hasInputs()) return; + ImporterOpts.BridgingHeaderIsInternal = FrontendOpts.ImportHeaderAsInternal; + // If we aren't asked to output a bridging header, we don't need to set this. if (ImporterOpts.PrecompiledHeaderOutputDir.empty()) return; @@ -2109,10 +2111,24 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args, else if (Args.hasArg(OPT_emit_pcm) || Args.hasArg(OPT_dump_pcm)) Opts.Mode = ClangImporterOptions::Modes::PrecompiledModule; - if (auto *A = Args.getLastArg(OPT_import_bridging_header)) + bool hadNormalBridgingHeader = false; + if (auto *A = Args.getLastArg(OPT_import_bridging_header, + OPT_internal_import_bridging_header)) { Opts.BridgingHeader = A->getValue(); - if (auto *A = Args.getLastArg(OPT_import_pch)) + Opts.BridgingHeaderIsInternal = + A->getOption().getID() == OPT_internal_import_bridging_header; + } + if (auto *A = Args.getLastArg(OPT_import_pch, OPT_internal_import_pch)) { Opts.BridgingHeaderPCH = A->getValue(); + bool importAsInternal = A->getOption().getID() == OPT_internal_import_pch; + if (hadNormalBridgingHeader && + importAsInternal != Opts.BridgingHeaderIsInternal) { + Diags.diagnose(SourceLoc(), + diag::bridging_header_and_pch_internal_mismatch); + } + Opts.BridgingHeaderIsInternal = importAsInternal; + } + Opts.DisableSwiftBridgeAttr |= Args.hasArg(OPT_disable_swift_bridge_attr); Opts.DisableOverlayModules |= Args.hasArg(OPT_emit_imported_modules); diff --git a/test/ClangImporter/Inputs/c-bridging-header.h b/test/ClangImporter/Inputs/c-bridging-header.h new file mode 100644 index 0000000000000..59f2717b40211 --- /dev/null +++ b/test/ClangImporter/Inputs/c-bridging-header.h @@ -0,0 +1,3 @@ +typedef struct { + double x, y; +} MyPoint; diff --git a/test/ClangImporter/InternalBridgingHeader/pch.swift b/test/ClangImporter/InternalBridgingHeader/pch.swift new file mode 100644 index 0000000000000..116393c057fb3 --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/pch.swift @@ -0,0 +1,50 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/tmp + +// First test the explicit frontend-based bridging PCH generation and use works +// RUN: %target-swift-frontend -emit-pch -o %t/c-bridging-header.pch %S/../Inputs/c-bridging-header.h +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %t/c-bridging-header.pch + +// Now test the driver-automated version is inert when disabled +// Output path of the PCH differs in the new driver, so force SWIFT_USE_OLD_DRIVER for now. +// RUN: env TMPDIR=%t/tmp/ SWIFT_USE_OLD_DRIVER=1 %target-swiftc_driver -typecheck -disable-bridging-pch -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h +// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1 + +// Test the driver-automated version works by default +// Output path of the PCH differs in the new driver, so force SWIFT_USE_OLD_DRIVER for now. +// RUN: env TMPDIR=%t/tmp/ SWIFT_USE_OLD_DRIVER=1 %target-swiftc_driver -typecheck -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h +// RUN: ls %t/tmp/*.pch >/dev/null 2>&1 +// RUN: llvm-objdump --raw-clang-ast %t/tmp/*.pch | llvm-bcanalyzer -dump | %FileCheck %s +// CHECK: ORIGINAL_FILE{{.*}}Inputs/c-bridging-header.h + +// Test the driver-automated version deletes its PCH file when done +// RUN: rm %t/tmp/*.pch +// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h +// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1 + +// Test -emit-pch invocation but with a persistent PCH +// RUN: %target-swift-frontend -emit-pch -pch-output-dir %t/pch %S/../Inputs/c-bridging-header.h +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch -pch-disable-validation +// RUN: ls %t/pch/*.pch >/dev/null 2>&1 + +// Test implicit use of persistent PCH +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch2 +// RUN: ls %t/pch2/*.pch >/dev/null 2>&1 + +// RUN: touch %t/header.with.dot.h +// RUN: touch %t/test.swift +// RUN: %target-swift-frontend -typecheck %t/test.swift -internal-import-bridging-header %t/header.with.dot.h -pch-output-dir %t/pch_with_dot -module-cache-path %t/mcp1 +// RUN: %target-swift-frontend -typecheck %t/test.swift -internal-import-bridging-header %t/header.with.dot.h -pch-output-dir %t/pch_with_dot -module-cache-path %t/mcp2 +// RUN: ls %t/pch_with_dot/*swift*clang*.pch | count 2 + +// Test the driver-automated version using persistent PCH +// RUN: %target-swiftc_driver -typecheck -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch3 +// RUN: ls %t/pch3/*.pch >/dev/null 2>&1 +// RUN: llvm-objdump --raw-clang-ast %t/pch3/*.pch | llvm-bcanalyzer -dump | %FileCheck %s -check-prefix=PERSISTENT +// PERSISTENT: ORIGINAL_FILE{{.*}}Inputs/c-bridging-header.h + +// Test that -pch-disable-validation works in that it won't implicitly create a PCH +// RUN: not %target-swift-frontend -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/no-pch -pch-disable-validation 2>&1 | %FileCheck %s -check-prefix=NO-VALIDATION +// NO-VALIDATION: PCH file {{.*}} not found + +func getX(point: MyPoint) -> Double { point.x } From b13c2aeccd4c11aa40ab972ce20b281fb9824e5f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 15:16:45 -0700 Subject: [PATCH 03/15] Treat internally-imported bridging headers as internally-imported When using an internal import for a bridging header, semantically treat the contents of the bridging header, and anything that it imports, as if they were imported internally. This is the actual semantic behavior we wanted from internally-imported bridging headers. This is the main semantic checking bit for rdar://74011750. --- include/swift/AST/DiagnosticsSema.def | 4 ++-- lib/AST/Module.cpp | 4 +++- lib/ClangImporter/ClangImporter.cpp | 8 ++++++- lib/Sema/ImportResolution.cpp | 8 ++++++- lib/Sema/TypeCheckAccess.cpp | 4 +++- test/ClangImporter/Inputs/c-bridging-header.h | 3 +++ .../access_checking.swift | 22 +++++++++++++++++ .../InternalBridgingHeader/pch.swift | 24 +++++++++---------- 8 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 test/ClangImporter/InternalBridgingHeader/access_checking.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 97bc1f14a4183..a7c07f0bbd7c2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2707,8 +2707,8 @@ NOTE(decl_import_via_here,none, NOTE(decl_import_via_local,none, "%kind0 is imported by this file as " "'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' " - "from %2", - (const Decl *, AccessLevel, const ModuleDecl*)) + "from %select{%2|bridging header}3", + (const Decl *, AccessLevel, const ModuleDecl*, bool)) // Opaque return types ERROR(opaque_type_invalid_constraint,none, diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 93409fb5eed9b..c3ed11ff240cd 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -2991,7 +2991,9 @@ SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const { // they are recommended over indirect imports. if ((!restrictiveImport.has_value() || restrictiveImport->accessLevel < AccessLevel::Public) && - imports.isImportedBy(targetModule, getParentModule())) + !(restrictiveImport && + restrictiveImport->module.importedModule->isClangHeaderImportModule()) && + imports.isImportedBy(targetModule, getParentModule())) return std::nullopt; return restrictiveImport; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index c01395fe30859..d3764de419077 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3763,8 +3763,14 @@ ImportDecl *swift::createImportDecl(ASTContext &Ctx, auto *ID = ImportDecl::create(Ctx, DC, SourceLoc(), ImportKind::Module, SourceLoc(), importPath.get(), ClangN); - if (IsExported) + if (Ctx.ClangImporterOpts.BridgingHeaderIsInternal) { + ID->getAttrs().add( + new (Ctx) AccessControlAttr(SourceLoc(), SourceRange(), + AccessLevel::Internal, /*implicit=*/true)); + ID->getAttrs().add(new (Ctx) ImplementationOnlyAttr(/*IsImplicit=*/true)); + } else if (IsExported) { ID->getAttrs().add(new (Ctx) ExportedAttr(/*IsImplicit=*/false)); + } return ID; } diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index e14ef37dfb046..245569d82cbec 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -574,8 +574,14 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, !clangImporter->importBridgingHeader(bridgingHeaderPath, module)) { auto *headerModule = clangImporter->getImportedHeaderModule(); assert(headerModule && "Didn't load bridging header?"); - imports.emplace_back( + AttributedImport import( ImportedModule(headerModule), SourceLoc(), ImportFlags::Exported); + if (ctx.ClangImporterOpts.BridgingHeaderIsInternal) { + import.accessLevel = AccessLevel::Internal; + import.options |= ImportFlags::ImplementationOnly; + } + + imports.emplace_back(import); } // Implicitly import the underlying Clang half of this module if needed. diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 4a844cf7b2e0f..2f73f0e55cf50 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -454,7 +454,9 @@ static void noteLimitingImport(const Decl *userDecl, ASTContext &ctx, if (userDecl) userDecl->diagnose(diag::decl_import_via_local, complainDecl, limitImport->accessLevel, - limitImport->module.importedModule); + limitImport->module.importedModule, + limitImport->module.importedModule + ->isClangHeaderImportModule()); if (limitImport->importLoc.isValid()) ctx.Diags.diagnose(limitImport->importLoc, diag::decl_import_via_here, diff --git a/test/ClangImporter/Inputs/c-bridging-header.h b/test/ClangImporter/Inputs/c-bridging-header.h index 59f2717b40211..763f74cfd8048 100644 --- a/test/ClangImporter/Inputs/c-bridging-header.h +++ b/test/ClangImporter/Inputs/c-bridging-header.h @@ -1,3 +1,6 @@ +#include "ctypes.h" +#include "macros.h" + typedef struct { double x, y; } MyPoint; diff --git a/test/ClangImporter/InternalBridgingHeader/access_checking.swift b/test/ClangImporter/InternalBridgingHeader/access_checking.swift new file mode 100644 index 0000000000000..8cd6b87f94aa2 --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/access_checking.swift @@ -0,0 +1,22 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/tmp + +// Test with the normal bridging header. +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk + +// Test with a precompiled bridging header. +// RUN: %target-swift-frontend -emit-pch -o %t/c-bridging-header.pch %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %t/c-bridging-header.pch -sdk %clang-importer-sdk + + +// Overrides the internal import that comes through the bridging header. +public import macros + +public func getRed() -> Color { red } // expected-error{{function cannot be declared public because its result uses an internal type}} +// expected-note@-1{{struct 'Color' is imported by this file as 'internal' from bridging header}} + +public func getX(point: MyPoint) -> Double { point.x } // expected-error{{function cannot be declared public because its parameter uses an internal type}} +// expected-note@-1{{struct 'MyPoint' is imported by this file as 'internal' from bridging header}} + +// Comes from the macros module. +public func returnsFromMacrosModule() -> okay_t { 0 } diff --git a/test/ClangImporter/InternalBridgingHeader/pch.swift b/test/ClangImporter/InternalBridgingHeader/pch.swift index 116393c057fb3..409e337ba5bc2 100644 --- a/test/ClangImporter/InternalBridgingHeader/pch.swift +++ b/test/ClangImporter/InternalBridgingHeader/pch.swift @@ -2,49 +2,49 @@ // RUN: mkdir -p %t/tmp // First test the explicit frontend-based bridging PCH generation and use works -// RUN: %target-swift-frontend -emit-pch -o %t/c-bridging-header.pch %S/../Inputs/c-bridging-header.h -// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %t/c-bridging-header.pch +// RUN: %target-swift-frontend -emit-pch -o %t/c-bridging-header.pch %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %t/c-bridging-header.pch -sdk %clang-importer-sdk // Now test the driver-automated version is inert when disabled // Output path of the PCH differs in the new driver, so force SWIFT_USE_OLD_DRIVER for now. -// RUN: env TMPDIR=%t/tmp/ SWIFT_USE_OLD_DRIVER=1 %target-swiftc_driver -typecheck -disable-bridging-pch -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h +// RUN: env TMPDIR=%t/tmp/ SWIFT_USE_OLD_DRIVER=1 %target-swiftc_driver -typecheck -disable-bridging-pch -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk // RUN: not ls %t/tmp/*.pch >/dev/null 2>&1 // Test the driver-automated version works by default // Output path of the PCH differs in the new driver, so force SWIFT_USE_OLD_DRIVER for now. -// RUN: env TMPDIR=%t/tmp/ SWIFT_USE_OLD_DRIVER=1 %target-swiftc_driver -typecheck -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h +// RUN: env TMPDIR=%t/tmp/ SWIFT_USE_OLD_DRIVER=1 %target-swiftc_driver -typecheck -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk // RUN: ls %t/tmp/*.pch >/dev/null 2>&1 // RUN: llvm-objdump --raw-clang-ast %t/tmp/*.pch | llvm-bcanalyzer -dump | %FileCheck %s // CHECK: ORIGINAL_FILE{{.*}}Inputs/c-bridging-header.h // Test the driver-automated version deletes its PCH file when done // RUN: rm %t/tmp/*.pch -// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h +// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk // RUN: not ls %t/tmp/*.pch >/dev/null 2>&1 // Test -emit-pch invocation but with a persistent PCH -// RUN: %target-swift-frontend -emit-pch -pch-output-dir %t/pch %S/../Inputs/c-bridging-header.h -// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch -pch-disable-validation +// RUN: %target-swift-frontend -emit-pch -pch-output-dir %t/pch %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch -pch-disable-validation -sdk %clang-importer-sdk // RUN: ls %t/pch/*.pch >/dev/null 2>&1 // Test implicit use of persistent PCH -// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch2 +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch2 -sdk %clang-importer-sdk // RUN: ls %t/pch2/*.pch >/dev/null 2>&1 // RUN: touch %t/header.with.dot.h // RUN: touch %t/test.swift -// RUN: %target-swift-frontend -typecheck %t/test.swift -internal-import-bridging-header %t/header.with.dot.h -pch-output-dir %t/pch_with_dot -module-cache-path %t/mcp1 -// RUN: %target-swift-frontend -typecheck %t/test.swift -internal-import-bridging-header %t/header.with.dot.h -pch-output-dir %t/pch_with_dot -module-cache-path %t/mcp2 +// RUN: %target-swift-frontend -typecheck %t/test.swift -internal-import-bridging-header %t/header.with.dot.h -pch-output-dir %t/pch_with_dot -module-cache-path %t/mcp1 -sdk %clang-importer-sdk +// RUN: %target-swift-frontend -typecheck %t/test.swift -internal-import-bridging-header %t/header.with.dot.h -pch-output-dir %t/pch_with_dot -module-cache-path %t/mcp2 -sdk %clang-importer-sdk // RUN: ls %t/pch_with_dot/*swift*clang*.pch | count 2 // Test the driver-automated version using persistent PCH -// RUN: %target-swiftc_driver -typecheck -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch3 +// RUN: %target-swiftc_driver -typecheck -save-temps %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/pch3 -sdk %clang-importer-sdk // RUN: ls %t/pch3/*.pch >/dev/null 2>&1 // RUN: llvm-objdump --raw-clang-ast %t/pch3/*.pch | llvm-bcanalyzer -dump | %FileCheck %s -check-prefix=PERSISTENT // PERSISTENT: ORIGINAL_FILE{{.*}}Inputs/c-bridging-header.h // Test that -pch-disable-validation works in that it won't implicitly create a PCH -// RUN: not %target-swift-frontend -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/no-pch -pch-disable-validation 2>&1 | %FileCheck %s -check-prefix=NO-VALIDATION +// RUN: not %target-swift-frontend -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/no-pch -pch-disable-validation -sdk %clang-importer-sdk 2>&1 | %FileCheck %s -check-prefix=NO-VALIDATION // NO-VALIDATION: PCH file {{.*}} not found func getX(point: MyPoint) -> Double { point.x } From 903937a78f7f5e6cb9b9495a383e9c12a9eeeae6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 15:57:17 -0700 Subject: [PATCH 04/15] Exempt C++ namespaces from internal-import checking C++ namespaces always get imported into the bridging header's module for Reasons. Exempt them from internal-import checking, since we get checking for the namespace members that are actually used. --- lib/Sema/TypeCheckAccess.cpp | 5 +++++ test/ClangImporter/Inputs/cxx-bridging-header.h | 9 +++++++++ test/ClangImporter/Inputs/cxx-outer-ns.h | 7 +++++++ test/ClangImporter/Inputs/module.modulemap | 3 +++ .../InternalBridgingHeader/access_checking_cxx.swift | 9 +++++++++ 5 files changed, 33 insertions(+) create mode 100644 test/ClangImporter/Inputs/cxx-bridging-header.h create mode 100644 test/ClangImporter/Inputs/cxx-outer-ns.h create mode 100644 test/ClangImporter/Inputs/module.modulemap create mode 100644 test/ClangImporter/InternalBridgingHeader/access_checking_cxx.swift diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 2f73f0e55cf50..31d67f840175e 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2694,6 +2694,11 @@ void swift::recordRequiredImportAccessLevelForDecl( if (definingModule == dc->getParentModule()) return; + // Egregious hack: if the declaration is for a C++ namespace, assume it's + // accessible. + if (isa_and_nonnull(decl->getClangDecl())) + return; + sf->registerRequiredAccessLevelForModule(definingModule, accessLevel); if (auto attributedImport = sf->getImportAccessLevel(definingModule)) { diff --git a/test/ClangImporter/Inputs/cxx-bridging-header.h b/test/ClangImporter/Inputs/cxx-bridging-header.h new file mode 100644 index 0000000000000..d792ba4ecc409 --- /dev/null +++ b/test/ClangImporter/Inputs/cxx-bridging-header.h @@ -0,0 +1,9 @@ + +namespace OuterNS { + class MyPoint { + public: + double x, y; + }; +} + +#include "cxx-outer-ns.h" diff --git a/test/ClangImporter/Inputs/cxx-outer-ns.h b/test/ClangImporter/Inputs/cxx-outer-ns.h new file mode 100644 index 0000000000000..d9d78404852f4 --- /dev/null +++ b/test/ClangImporter/Inputs/cxx-outer-ns.h @@ -0,0 +1,7 @@ +namespace OuterNS { + enum Color { + red, + green, + blue + }; +} diff --git a/test/ClangImporter/Inputs/module.modulemap b/test/ClangImporter/Inputs/module.modulemap new file mode 100644 index 0000000000000..296486613ab8c --- /dev/null +++ b/test/ClangImporter/Inputs/module.modulemap @@ -0,0 +1,3 @@ +module CXXOuterNS { + header "cxx-outer-ns.h" +} diff --git a/test/ClangImporter/InternalBridgingHeader/access_checking_cxx.swift b/test/ClangImporter/InternalBridgingHeader/access_checking_cxx.swift new file mode 100644 index 0000000000000..fb355fd625db5 --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/access_checking_cxx.swift @@ -0,0 +1,9 @@ +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/cxx-bridging-header.h -sdk %clang-importer-sdk -cxx-interoperability-mode=default -I %S/../Inputs + +public func getRed() -> OuterNS.Color { OuterNS.red } +// expected-error@-1{{function cannot be declared public because its result uses an internal type}} +// expected-note@-2{{enum 'OuterNS' is imported by this file as 'internal' from bridging header}} + +public func getX(point: OuterNS.MyPoint) -> Double { point.x } +// expected-error@-1{{function cannot be declared public because its parameter uses an internal type}} +// expected-note@-2{{enum 'OuterNS' is imported by this file as 'internal' from bridging header}} From b9f00ef9236a56ea2aedec1bc2d7d312f7e9fc62 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 16:19:17 -0700 Subject: [PATCH 05/15] Warn about the use of internal bridging headers without library evolution It's very, very easy to make a mistake that will cause broken serialized modules. Until that's no longer true, at least tell folks that they are heading into uncharted waters, as we do with `@_implementationOnly` imports. --- include/swift/AST/DiagnosticsFrontend.def | 3 +++ lib/Frontend/CompilerInvocation.cpp | 7 +++++++ .../InternalBridgingHeader/library_evolution.swift | 14 ++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 test/ClangImporter/InternalBridgingHeader/library_evolution.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 754ea7d3780d4..c2ebf1b73e9bb 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -609,6 +609,9 @@ ERROR(no_swift_sources_with_embedded,none, ERROR(package_cmo_requires_library_evolution, none, "Library evolution must be enabled for Package CMO", ()) +WARNING(internal_bridging_header_without_library_evolution,none, + "using internal bridging headers without library evolution can cause instability", ()) + ERROR(experimental_not_supported_in_production,none, "experimental feature '%0' cannot be enabled in production compiler", (StringRef)) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 697536b1a93dc..0f7e155b9c2da 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2129,6 +2129,13 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args, Opts.BridgingHeaderIsInternal = importAsInternal; } + // Until we have some checking in place, internal bridging headers are a + // bit unsafe without library evolution. + if (Opts.BridgingHeaderIsInternal && !FrontendOpts.EnableLibraryEvolution) { + Diags.diagnose(SourceLoc(), + diag::internal_bridging_header_without_library_evolution); + } + Opts.DisableSwiftBridgeAttr |= Args.hasArg(OPT_disable_swift_bridge_attr); Opts.DisableOverlayModules |= Args.hasArg(OPT_emit_imported_modules); diff --git a/test/ClangImporter/InternalBridgingHeader/library_evolution.swift b/test/ClangImporter/InternalBridgingHeader/library_evolution.swift new file mode 100644 index 0000000000000..df3b9de4bf58e --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/library_evolution.swift @@ -0,0 +1,14 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk %s 2>&1 | %FileCheck -check-prefix NONRESILIENT %s + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk %s -enable-library-evolution 2>&1 | %FileCheck -check-prefix EVOLUTION %s + +// NONRESILIENT: warning: using internal bridging headers without library evolution can cause instability + +// EVOLUTION-NOT: internal bridging head + +@available(*, deprecated) +func f() { } + +func g(){ + f() // make sure we emit something +} From 30bdb6b37a20b793e2a3db399d057488db614f66 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 16:36:52 -0700 Subject: [PATCH 06/15] [Serialization] Don't serialize an internally-imported bridging header Internally-imported bridging headers must not leak outside of the Swift module. Don't serialize their contents, and make sure we can still import the module even if the bridging header has been removed. --- lib/Frontend/Frontend.cpp | 3 +- .../serialization.swift | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/ClangImporter/InternalBridgingHeader/serialization.swift diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 5ce141e45a407..ff0ba284205fd 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -197,7 +197,8 @@ SerializationOptions CompilerInvocation::computeSerializationOptions( serializationOpts.DocOutputPath = outs.ModuleDocOutputPath; serializationOpts.SourceInfoOutputPath = outs.ModuleSourceInfoOutputPath; serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str(); - if (opts.ModuleHasBridgingHeader && !outs.ModuleOutputPath.empty()) + if (opts.ModuleHasBridgingHeader && !outs.ModuleOutputPath.empty() && + !opts.ImportHeaderAsInternal) serializationOpts.SerializeBridgingHeader = true; // For batch mode, emit empty header path as placeholder. if (serializationOpts.SerializeBridgingHeader && diff --git a/test/ClangImporter/InternalBridgingHeader/serialization.swift b/test/ClangImporter/InternalBridgingHeader/serialization.swift new file mode 100644 index 0000000000000..499d2b883a4ce --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/serialization.swift @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) + +// Stage in the headers we need +// RUN: mkdir %t/headers +// RUN: cp %S/../Inputs/c-bridging-header.h %t/headers + +// RUN: mkdir %t/src +// RUN: split-file %s %t/src + +// Build a module +// RUN: mkdir %t/modules +// RUN: %target-swift-frontend -internal-import-bridging-header %t/headers/c-bridging-header.h -sdk %clang-importer-sdk -emit-module -o %t/modules/MyModule.swiftmodule %t/src/MyModule.swift + +// Check that there's no serialized bridging header in the module file. +// RUN: llvm-bcanalyzer -dump %t/modules/MyModule.swiftmodule | %FileCheck -check-prefix MODULE-FILE %s + +// Use the module. +// RUN: %target-swift-frontend -typecheck -sdk %clang-importer-sdk -I %t/modules %t/src/MyClient.swift + +// Delete the bridging header, and again use the module. +// RUN: rm %t/headers/c-bridging-header.h +// RUN: %target-swift-frontend -typecheck -sdk %clang-importer-sdk -I %t/modules %t/src/MyClient.swift + +//--- MyModule.swift +func getRed() -> Color { red } + +func getX(point: MyPoint) -> Double { point.x } + +public func f() { + _ = getRed() +} + +//--- MyClient.swift + +import MyModule + +func g() { + f() +} + + +// MODULE-FILE-NOT: IMPORTED_HEADER +// MODULE-FILE-NOT: IMPORTED_HEADER_CONTENTS + +// MODULE-FILE: SEARCH_PATH + +// MODULE-FILE-NOT: IMPORTED_HEADER +// MODULE-FILE-NOT: IMPORTED_HEADER_CONTENTS From 7bc02d6a70d0e67a5b2ffa79f425ad816d254bed Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 16:48:22 -0700 Subject: [PATCH 07/15] Improve some diagnostic with @_implementationOnly violations from internal bridging headers --- include/swift/AST/DiagnosticsSema.def | 4 ++-- lib/Sema/ResilienceDiagnostics.cpp | 16 ++++++++++---- lib/Sema/TypeCheckAccess.cpp | 4 +++- .../implementation_only_checking.swift | 21 +++++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 test/ClangImporter/InternalBridgingHeader/implementation_only_checking.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index a7c07f0bbd7c2..f1f31cd870cdd 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2702,8 +2702,8 @@ NOTE(module_imported_here,none, NOTE(decl_import_via_here,none, "%kind0 imported as " "'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' " - "from %2 here", - (const Decl *, AccessLevel, const ModuleDecl*)) + "from %select{%2 here|bridging header}3", + (const Decl *, AccessLevel, const ModuleDecl*, bool)) NOTE(decl_import_via_local,none, "%kind0 is imported by this file as " "'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' " diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 5fb3249585144..b93d02c71e988 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -133,7 +133,9 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, Context.Diags.diagnose(problematicImport->importLoc, diag::decl_import_via_here, D, problematicImport->accessLevel, - problematicImport->module.importedModule); + problematicImport->module.importedModule, + problematicImport->module.importedModule + ->isClangHeaderImportModule()); } return (downgradeToWarning == DowngradeToWarning::No); @@ -213,7 +215,9 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc, ctx.Diags.diagnose(limitImport->importLoc, diag::decl_import_via_here, D, limitImport->accessLevel, - limitImport->module.importedModule); + limitImport->module.importedModule, + limitImport->module.importedModule + ->isClangHeaderImportModule()); } return true; @@ -368,7 +372,9 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D, ctx.Diags.diagnose(import->importLoc, diag::decl_import_via_here, D, import->accessLevel, - import->module.importedModule); + import->module.importedModule, + import->module.importedModule + ->isClangHeaderImportModule()); } return true; @@ -454,7 +460,9 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc, ctx.Diags.diagnose(limitImport->importLoc, diag::decl_import_via_here, ext, limitImport->accessLevel, - limitImport->module.importedModule); + limitImport->module.importedModule, + limitImport->module.importedModule + ->isClangHeaderImportModule()); } return true; diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 31d67f840175e..4a9fd36a50e91 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -461,7 +461,9 @@ static void noteLimitingImport(const Decl *userDecl, ASTContext &ctx, if (limitImport->importLoc.isValid()) ctx.Diags.diagnose(limitImport->importLoc, diag::decl_import_via_here, complainDecl, limitImport->accessLevel, - limitImport->module.importedModule); + limitImport->module.importedModule, + limitImport->module.importedModule + ->isClangHeaderImportModule()); } else if (limitImport->importLoc.isValid()) { ctx.Diags.diagnose(limitImport->importLoc, diag::module_imported_here, limitImport->module.importedModule, diff --git a/test/ClangImporter/InternalBridgingHeader/implementation_only_checking.swift b/test/ClangImporter/InternalBridgingHeader/implementation_only_checking.swift new file mode 100644 index 0000000000000..ba10fe9969c62 --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/implementation_only_checking.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/tmp + +// Test with the normal bridging header. +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk -verify-ignore-unknown + +// RUN: not %target-swift-frontend -typecheck -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk %s 2>&1 | %FileCheck %s + +// Test with a precompiled bridging header. +// RUN: %target-swift-frontend -emit-pch -o %t/c-bridging-header.pch %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk +// RUN: %target-typecheck-verify-swift -internal-import-bridging-header %t/c-bridging-header.pch -sdk %clang-importer-sdk -verify-ignore-unknown + + +@inlinable +public func f() -> Any { + return red + // expected-error@-1{{var 'red' is internal and cannot be referenced from an '@inlinable' function}} + // expected-warning@-2{{getter for var 'red' is internal and should not be referenced from an '@inlinable' function}} +} + +// CHECK: var 'red' imported as 'internal' from bridging header From 52ebe09cddb7a35803fe0a52b5b8a44b75847b29 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 17:08:37 -0700 Subject: [PATCH 08/15] Don't mix internal and @_implementationOnly --- lib/Sema/ImportResolution.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index 245569d82cbec..9811296b1611d 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -578,7 +578,6 @@ ModuleImplicitImportsRequest::evaluate(Evaluator &evaluator, ImportedModule(headerModule), SourceLoc(), ImportFlags::Exported); if (ctx.ClangImporterOpts.BridgingHeaderIsInternal) { import.accessLevel = AccessLevel::Internal; - import.options |= ImportFlags::ImplementationOnly; } imports.emplace_back(import); From d04e6dd8810dca41041be64a951b83f5e30f8032 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 19 Sep 2025 20:09:59 -0700 Subject: [PATCH 09/15] Add #include guards around bridging header --- test/ClangImporter/Inputs/c-bridging-header.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/ClangImporter/Inputs/c-bridging-header.h b/test/ClangImporter/Inputs/c-bridging-header.h index 763f74cfd8048..c12165cff80ea 100644 --- a/test/ClangImporter/Inputs/c-bridging-header.h +++ b/test/ClangImporter/Inputs/c-bridging-header.h @@ -1,6 +1,11 @@ +#ifndef C_BRIDGING_HEADER_H +#define C_BRIDGING_HEADER_H + #include "ctypes.h" #include "macros.h" typedef struct { double x, y; } MyPoint; + +#endif From b7a83495fa58b927f05346161ac67760f5356779 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 10:38:06 -0700 Subject: [PATCH 10/15] Expand DisallowedOriginKind with an explicit entry for internal bridging headers --- include/swift/AST/DiagnosticsSema.def | 5 +++++ lib/Sema/ResilienceDiagnostics.cpp | 4 +++- lib/Sema/TypeCheckAccess.cpp | 7 +++++-- lib/Sema/TypeCheckAccess.h | 4 ++++ test/ClangImporter/Inputs/c-bridging-header.h | 1 + .../access_checking.swift | 17 +++++++++++++++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f1f31cd870cdd..92dfb54da7e8f 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3834,6 +3834,7 @@ ERROR(decl_from_hidden_module,none, "%2 was imported for SPI only|" "%2 was not imported by this file|" "C++ types from imported module %2 do not support library evolution|" + "it was imported via the internal bridging header|" "%2 was not imported publicly}3", (const Decl *, unsigned, Identifier, unsigned)) ERROR(typealias_desugars_to_type_from_hidden_module,none, @@ -3850,6 +3851,7 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none, "%4 was imported for SPI only|" "%4 was not imported by this file|" "C++ types from imported module %4 do not support library evolution|" + "it was imported via the internal bridging header|" "%4 was not imported publicly}5", (const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned)) ERROR(conformance_from_implementation_only_module,none, @@ -3864,6 +3866,7 @@ ERROR(conformance_from_implementation_only_module,none, "%3 was imported for SPI only|" "%3 was not imported by this file|" "C++ types from imported module %3 do not support library evolution|" + "it was imported via the internal bridging header|" "%3 was not imported publicly}4", (Type, Identifier, unsigned, Identifier, unsigned)) NOTE(assoc_conformance_from_implementation_only_module,none, @@ -7317,6 +7320,7 @@ ERROR(inlinable_decl_ref_from_hidden_module, "%2 was imported for SPI only|" "%2 was not imported by this file|" "C++ APIs from imported module %2 do not support library evolution|" + "it was imported via the internal bridging header|" "%2 was not imported publicly}3", (const ValueDecl *, unsigned, Identifier, unsigned)) @@ -7328,6 +7332,7 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module, "%4 was imported for SPI only|" "%4 was not imported by this file|" "C++ types from imported module %4 do not support library evolution|" + "it was imported via the internal bridging header|" "%4 was not imported publicly}5", (const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned)) diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index b93d02c71e988..3a4dbd1112107 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -277,7 +277,8 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D, D, DC, AccessLevel::Public, [&](AttributedImport attributedImport) { if (where.isExported() && reason != ExportabilityReason::General && - originKind != DisallowedOriginKind::NonPublicImport) { + originKind != DisallowedOriginKind::NonPublicImport && + originKind != DisallowedOriginKind::InternalBridgingHeaderImport) { // These may be reported twice, for the Type and for the TypeRepr. ModuleDecl *importedVia = attributedImport.module.importedModule, *sourceModule = D->getModuleContext(); @@ -294,6 +295,7 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D, return false; case DisallowedOriginKind::NonPublicImport: + case DisallowedOriginKind::InternalBridgingHeaderImport: // With a few exceptions, access levels from imports are diagnosed during // access checking and should be skipped here. if (!shouldDiagnoseDeclAccess(D, where)) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 4a9fd36a50e91..f7c12060e4ac5 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -2167,8 +2167,11 @@ swift::getDisallowedOriginKind(const Decl *decl, // See \c diagnoseValueDeclRefExportability. auto importSource = decl->getImportAccessFrom(where.getDeclContext()); if (importSource.has_value() && - importSource->accessLevel < AccessLevel::Public) - return DisallowedOriginKind::NonPublicImport; + importSource->accessLevel < AccessLevel::Public) { + return importSource->module.importedModule->isClangHeaderImportModule() + ? DisallowedOriginKind::InternalBridgingHeaderImport + : DisallowedOriginKind::NonPublicImport; + } return DisallowedOriginKind::None; } diff --git a/lib/Sema/TypeCheckAccess.h b/lib/Sema/TypeCheckAccess.h index 6291c1de9d419..c97312b962370 100644 --- a/lib/Sema/TypeCheckAccess.h +++ b/lib/Sema/TypeCheckAccess.h @@ -47,6 +47,10 @@ enum class DisallowedOriginKind : uint8_t { SPIOnly, MissingImport, FragileCxxAPI, + + /// An import that is internal via the internally-imported bridging header. + InternalBridgingHeaderImport, + NonPublicImport, None }; diff --git a/test/ClangImporter/Inputs/c-bridging-header.h b/test/ClangImporter/Inputs/c-bridging-header.h index c12165cff80ea..5dc00a6d1a586 100644 --- a/test/ClangImporter/Inputs/c-bridging-header.h +++ b/test/ClangImporter/Inputs/c-bridging-header.h @@ -8,4 +8,5 @@ typedef struct { double x, y; } MyPoint; +typedef double MyDouble; #endif diff --git a/test/ClangImporter/InternalBridgingHeader/access_checking.swift b/test/ClangImporter/InternalBridgingHeader/access_checking.swift index 8cd6b87f94aa2..237536cec4ef7 100644 --- a/test/ClangImporter/InternalBridgingHeader/access_checking.swift +++ b/test/ClangImporter/InternalBridgingHeader/access_checking.swift @@ -20,3 +20,20 @@ public func getX(point: MyPoint) -> Double { point.x } // expected-error{{functi // Comes from the macros module. public func returnsFromMacrosModule() -> okay_t { 0 } + +public protocol P { + associatedtype A +} + +public struct MyType: P { + public typealias A = MyDouble + // expected-error@-1{{type alias cannot be declared public because its underlying type uses an internal type}} + // expected-note@-2{{type alias 'MyDouble' is imported by this file as 'internal' from bridging header}} +} + +// expected-error@+1{{cannot use struct 'MyPoint' in an extension with public or '@usableFromInline' members; it was imported via the internal bridging header}} +extension MyPoint: P { + public typealias A = MyDouble + // expected-error@-1{{type alias cannot be declared public because its underlying type uses an internal type}} + // expected-note@-2{{type alias 'MyDouble' is imported by this file as 'internal' from bridging header}} +} From 085f3b467af4de3cbacb04e2015b965b4543bc66 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 16:23:27 -0700 Subject: [PATCH 11/15] Add -internal-import-bridging-header as a supported feature --- lib/Option/features.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Option/features.json b/lib/Option/features.json index f9c87c00022b9..7d521787a4dd8 100644 --- a/lib/Option/features.json +++ b/lib/Option/features.json @@ -50,6 +50,9 @@ }, { "name": "Isystem" + }, + { + "name": "internal-import-bridging-header" } ] } From a1bc5777203773fef190e15437466298b7f71c70 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 16:32:47 -0700 Subject: [PATCH 12/15] Ensure that -internal-import-bridging-header doesn't show up in a textual interface --- .../InternalBridgingHeader/interface.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test/ClangImporter/InternalBridgingHeader/interface.swift diff --git a/test/ClangImporter/InternalBridgingHeader/interface.swift b/test/ClangImporter/InternalBridgingHeader/interface.swift new file mode 100644 index 0000000000000..f6ea1825348fe --- /dev/null +++ b/test/ClangImporter/InternalBridgingHeader/interface.swift @@ -0,0 +1,14 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-emit-module-interface(%t/MyModule.swiftinterface) %s -module-name MyModule -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -sdk %clang-importer-sdk -enable-library-evolution + +// RUN: %FileCheck %s < %t/MyModule.swiftinterface + +// CHECK-NOT: internal-import-bridging-header + +// CHECK: public func g() + +func getX(point: MyPoint) -> Double { point.x } + +public func g() { +} From 67a9fe09d6a3d5e93e6fe45cb97954201ac45bbf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 16:36:05 -0700 Subject: [PATCH 13/15] Temporarily disable on Windows --- test/ClangImporter/InternalBridgingHeader/pch.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ClangImporter/InternalBridgingHeader/pch.swift b/test/ClangImporter/InternalBridgingHeader/pch.swift index 409e337ba5bc2..d0b65ffe8a993 100644 --- a/test/ClangImporter/InternalBridgingHeader/pch.swift +++ b/test/ClangImporter/InternalBridgingHeader/pch.swift @@ -47,4 +47,6 @@ // RUN: not %target-swift-frontend -typecheck %s -internal-import-bridging-header %S/../Inputs/c-bridging-header.h -pch-output-dir %t/no-pch -pch-disable-validation -sdk %clang-importer-sdk 2>&1 | %FileCheck %s -check-prefix=NO-VALIDATION // NO-VALIDATION: PCH file {{.*}} not found +// UNSUPPORTED: OS=windows-msvc + func getX(point: MyPoint) -> Double { point.x } From 511ce96f92703892828db118aa5b7e323517d9e5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 17:08:27 -0700 Subject: [PATCH 14/15] Remove unnecessary addition of @_implementationOnly --- lib/ClangImporter/ClangImporter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index d3764de419077..19449f9365094 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3767,7 +3767,6 @@ ImportDecl *swift::createImportDecl(ASTContext &Ctx, ID->getAttrs().add( new (Ctx) AccessControlAttr(SourceLoc(), SourceRange(), AccessLevel::Internal, /*implicit=*/true)); - ID->getAttrs().add(new (Ctx) ImplementationOnlyAttr(/*IsImplicit=*/true)); } else if (IsExported) { ID->getAttrs().add(new (Ctx) ExportedAttr(/*IsImplicit=*/false)); } From 87cbe5d2a94b44ce967d78aebde404de314e2bd9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 17:10:55 -0700 Subject: [PATCH 15/15] Clean up help text for new option --- include/swift/Option/Options.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 9efd7fd8eb90b..e99cdc850671f 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -356,7 +356,7 @@ def import_objc_header : Separate<["-"], "import-objc-header">, def internal_import_bridging_header : Separate<["-"], "internal-import-bridging-header">, Flags<[FrontendOption, ArgumentIsPath]>, - HelpText<"Imports an C header file as an internal import">; + HelpText<"Implicitly imports a C header file as an internal import">; def import_pch : Separate<["-"], "import-pch">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>,