Skip to content

#[export_visibility = ...] attribute #881

Closed
@anforowicz

Description

@anforowicz

Proposal

I propose to add #[export_visibility = …] attribute, which could be used to override the default visibility that is currently forced whenever using #[export_name = …] or #[no_mangle].

I propose that the new attribute can only be applied to items that are exported as SymbolExportLevel::C / contains_extern_indicator.

I propose that the visibility would be computed as follows:

  • Regular function (without the new attribute, and without #[export_name = …] nor #[no_mangle]): no changes = visibility is based on the platform, or on the -Zdefault-visibility=... cmdline flag
  • #[export_name = …] or #[no_mangle] without the new attribute: no changes = public export
  • #[export_name = …] or #[no_mangle] with the new attribute:
    • Any of the values supported by -Zdefault-visibility=... (e.g. hidden, protected, or interposable) would result in this specific visibility.
    • The special #[export_visibility = “inherit”] value would result in using the target platform’s or cmdline flag’s visibility (basically this would mean: don’t let #[export_name] / #[no_mangle] affect the baseline visibility).

Motivation

I believe that this proposal is a tractable way to resolve rust-lang/rust#73958. (The direct motivation for me is https://crbug.com/418073233 which has the same root cause.)

Feedback please?

I hope to get 3 kinds of feedback:

  • Process feedback. For example:

    • Whether instead of this Major Change Proposal (MCP), maybe I should have submitted a Request For Comments (RFC) as described here? If so, then hopefully the MCP discussion will help to start this in the right way.
    • What specific next steps/actions should I take if this MCP seems desirable to pursue? For example - I assume that before raising https://github.com/anforowicz/rust/tree/export-visibility as a pull request, I should first take care of some prerequisites, but I am not sure what those are and what their ordering should be (e.g. getting some kind of an indication of an agreement about the proposed approach? editing the https://doc.rust-lang.org/stable/unstable-book/? open a tracking issue for the new unstable feature at https://github.com/rust-lang/rust/issues?)
  • High-level / directional feedback. For example:

    • Whether one of the alternatives discussed below should be pursued instead? In other words, whether having a per-symbol mechanism to specify the visibility is the best way forward?
    • Whether there are some unforeseen, high-level issues with the #[export_visibility = …]-based approach?
    • Whether the proposal seems like something that could be adopted by cxx? (/cc @dtolnay)
      • #[export_visibility = …] in cxx/src/symbols can potentially be controlled by a new crate-level feature
      • #[export_visibility = …] in #[cxx::bridge]-generated code may be a bit trickier: it is unclear 1) how to opt into emitting #[export_visibility = …] (cxxbridge-macro build-time crate feature? #[cxx::bridge(..., export_visibility = …)]? Some other mechanism?) and 2) who/where is responsible for uttering the #![feature(export_visibility)] incantation to opt into the proposed unstable feature.
      • See changes in https://crrev.com/c/6580611/2 under third_party/rust/chromium_crates_io/vendor/… for an example of a quick&dirty way to test the new attribute in cxx.
  • Low-level / technical feedback. For example:

    • How to name the “inherit” visibility? (Other ideas: “baseline”, “preset”, “inherent”, “intrinsic”)
    • How to name the new attribute? (export_visibility? link_visibility? symbol_visibility? else?)

Alternatives

Instead of adopting this proposal, we could also consider one of the following alternatives:

  • Do nothing. For example, cxx#1500 example dtolnay/cxx#1520 demonstrates how to work around the problem by controlling symbol visibility with version scripts passed to the linker.
  • Change the semantics of #[export_name = …] and/or #[no_mangle] at an edition boundary.
    • I think this is what is proposed in Ability from the top-level of the compilation not to mark #[no_mangle] items exported from shared library rust#73958 (comment). In my initial reply to this alternative proposal it seemed to me that this will only work when linking is driven by rustc rather than by an external linker such as ldd - I am now thinking that maybe I was wrong about this. But I still don’t understand why global, link-time reasoning is needed (e.g. considering “symbols reachable from the public API of the top-level crate”)
    • Maybe we can instead consider a simpler proposal of changing the meaning of #[export_name = …] and #[no_mangle] so that:
      • In editions <= 2024 the current behavior is preserved (symbol is publicly exported)
      • In edition >= 2027 those naming attributes wouldn’t have any symbol/linker visibility impact on their own (which gives us the desired semantics combined with -Zdefault-visibility=...). If a publicly exported symbol was desirable, then this could be expressed with either A) #[used] or B) #[export_visibility = …].
    • At any rate, changing the semantics of #[export_name = …] in a future Rust edition seems compatible with the #[export_visibility = …] proposal. It seems that the visibility attribute would be useful in the current world and may also continue to be useful in the (hypothetical) new edition behavior (in both cases/editions overriding the default behavior of #[export_name = …] may sometimes be desirable).
  • -Zdefault-visibility-for-c-exports=...
    • This would offer a global (rather than per-symbol) override of the semantics of #[export_name = …] and #[no_mangle]. Implementation-wise, I think that this could be done by changing the code here
    • This alternative seems suboptimal, because one may want to 1) hide all cxx-generated symbols, but still 2) export some other symbols.
  • We could also consider a different spelling of an attribute-based approach - maybe one that avoids introducing a new attribute:

Other notes

Mentors or Reviewers

Maybe @bjorn3, who has participated in the discussion in rust-lang/rust#73958?

Process

The main points of the Major Change Process are as follows:

  • File an issue describing the proposal.
  • A compiler team member or contributor who is knowledgeable in the area can second by writing @rustbot second.
    • Finding a "second" suffices for internal changes. If however, you are proposing a new public-facing feature, such as a -C flag, then full team check-off is required.
    • Compiler team members can initiate a check-off via @rfcbot fcp merge on either the MCP or the PR.
  • Once an MCP is seconded, the Final Comment Period begins. If no objections are raised after 10 days, the MCP is considered approved.

You can read more about Major Change Proposals on forge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-compilerAdd this label so rfcbot knows to poll the compiler teammajor-changeA proposal to make a major change to rustc

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions