Description
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
, orinterposable
) 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).
- Any of the values supported by
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 = …]
incxx/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 incxx
.
-
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 asldd
- 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).
- 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
-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.
- This would offer a global (rather than per-symbol) override of the semantics of
- We could also consider a different spelling of an attribute-based approach - maybe one that avoids introducing a new attribute:
#[link_name = …]
could be allowed on definitions (IIUC it is only allowed onextern “C”
declarations today). And then#[link_name = …]
could mean the same thing as#[export_name = …] #[export_visibility = “inherit”]
from this proposal.- Extend the
#[used]
attribute so that it can specify the scope at which a symbol should be retained / considered used. (This is inspired by the ideas shared in Ability from the top-level of the compilation not to mark #[no_mangle] items exported from shared library rust#73958 (comment))
Other notes
- I think that the new attribute doesn’t need to be treated as
unsafe
(i.e. it seems sufficient that#[export_name = …]
and#[no_mangle]
areunsafe
). But maybe this aspect should be treated as an open question (to be resolved before stabilization). - I have tried prototyping this approach in https://github.com/anforowicz/rust/tree/export-visibility. I have tested that this prototype does indeed fix the issue described in https://crbug.com/418073233 - see my notes in https://crbug.com/418073233#comment12.
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.
- Finding a "second" suffices for internal changes. If however, you are proposing a new public-facing feature, such as a
- 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.