Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking issue for internal feature no_core #29639

Open
aturon opened this issue Nov 5, 2015 · 29 comments
Open

Tracking issue for internal feature no_core #29639

aturon opened this issue Nov 5, 2015 · 29 comments
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC S-tracking-needs-summary Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@aturon
Copy link
Member

aturon commented Nov 5, 2015

The no_core feature allows you to avoid linking to libcore.

@aturon aturon added T-lang Relevant to the language team, which will review and decide on the PR/issue. B-unstable Blocker: Implemented in the nightly compiler and unstable. labels Nov 5, 2015
@ahmedcharles
Copy link
Contributor

Perhaps it would be better to give this a 'positive' name, like [freestanding]?

@eefriedman
Copy link
Contributor

no_core is basically useless on its own at the moment; you need lang_items to actually implement enough of the core traits to allow compiling anything non-trivial.

@steveklabnik
Copy link
Member

Traige: no change

@Mark-Simulacrum Mark-Simulacrum added the C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC label Jul 22, 2017
@gnzlbg
Copy link
Contributor

gnzlbg commented Mar 7, 2019

no_core is basically useless on its own at the moment; you need lang_items to actually implement enough of the core traits to allow compiling anything non-trivial.

#[no_core] is super useful: some crates do not use any lang items and compile just fine (e.g. cfg-if), and if these crates aren't #[no_core] then we can't use them as dependencies of libcore, which means we have to duplicate them. For example, we currently duplicate cfg-if in libcore, libstd, stdsimd, and in libc (EDIT: and probably in hashbrown as well, that's 5 times that this macro is duplicated).

If we make cfg-if #[no_core] we can just add it as a normal cargo dependency to all those crates, and everything "just works". But we can't make it #[no_core] because it is not stable.

@Ericson2314
Copy link
Contributor

@gnzlbg makes a great point.

Additionally I'd like it if it was renamed to not mention the name of any create. If we get core depending on more crates, core might not seem so core-y. Something like no_dependencies is more future proof.

On the other hand, maybe we'll someday want to make primitives like float only available from come crate, removing them from the ambient prelude. That would mean the current name is more forward compatible after all.

@kotauskas
Copy link

This idea seems fishy and impractical for me, let me quote @Ericson2314 to elaborate why:

If we get core depending on more crates, core might not seem so core-y.

If core requires a certain crate for its convenience features and that crate is small enough to be used by core and to not depend on core at all, then why wouldn't core just include that crate into itself as an RFC to the language as a whole? If core depends on such a crate, therefore all of its members are linked to core and take the disk space in the final executable, meaning that not there's totally no reason to avoid using such a crate even if it's used only briefly in a very specific place where it's not that necessary which is why people rewrite a subset functionality from existing crates themselves for their specific use case.

This is not true for procedural macros, which are loaded into the compiler when core itself is compiled but are completely transparent to the final application, but either way there's the problem with the "standard" part in "standard library". The point of it is to provide utilities and core language features which are useful in a wide range of programs. Now, admittedly, cfg_if performs conditional compilation which is typically only used in abstractions over platform-specific code and that is supposed to implemented by a few crates and then reused by many other crates, meaning that the total amount of code which uses cfg_if is relatively low. But since it's such a small thing which was found useful in core, maybe it can become part of its public API, solving the duplication problem, providing access to its (unchanging!) interface for all Rust programs and promoting the use of it in a wider range of crates, improving readability. no_core would only promote hiding useful macros as private dependencies of core instead of promoting those macros becoming a part of Rust.

@ojeda
Copy link
Contributor

ojeda commented Jan 14, 2021

If we make cfg-if #[no_core] we can just add it as a normal cargo dependency to all those crates, and everything "just works".

Please don't make core depend on anything unless there is a very good reason for it, much less on external repositories or on a particular build system.

github-actions bot pushed a commit to rust-lang/glacier that referenced this issue Jul 13, 2021
=== stdout ===
=== stderr ===
error[E0658]: language items are subject to change
 --> /home/runner/work/glacier/glacier/ices/86238.rs:6:1
  |
6 | #[lang = "sized"]
  | ^^^^^^^^^^^^^^^^^
  |
  = help: add `#![feature(lang_items)]` to the crate attributes to enable

error[E0658]: language items are subject to change
 --> /home/runner/work/glacier/glacier/ices/86238.rs:8:1
  |
8 | #[lang = "copy"]
  | ^^^^^^^^^^^^^^^^
  |
  = help: add `#![feature(lang_items)]` to the crate attributes to enable

error[E0658]: the `#[no_core]` attribute is an experimental feature
 --> /home/runner/work/glacier/glacier/ices/86238.rs:1:1
  |
1 | #![no_core]
  | ^^^^^^^^^^^
  |
  = note: see issue #29639 <rust-lang/rust#29639> for more information
  = help: add `#![feature(no_core)]` to the crate attributes to enable

error: failed to find an overloaded call trait for closure call
 --> /home/runner/work/glacier/glacier/ices/86238.rs:4:5
  |
4 |     one()
  |     ^^^^^
  |
  = help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have associated `call`/`call_mut`/`call_once` functions

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0658`.
==============
JohnTitor pushed a commit to rust-lang/glacier that referenced this issue Jul 13, 2021
=== stdout ===
=== stderr ===
error[E0658]: language items are subject to change
 --> /home/runner/work/glacier/glacier/ices/86238.rs:6:1
  |
6 | #[lang = "sized"]
  | ^^^^^^^^^^^^^^^^^
  |
  = help: add `#![feature(lang_items)]` to the crate attributes to enable

error[E0658]: language items are subject to change
 --> /home/runner/work/glacier/glacier/ices/86238.rs:8:1
  |
8 | #[lang = "copy"]
  | ^^^^^^^^^^^^^^^^
  |
  = help: add `#![feature(lang_items)]` to the crate attributes to enable

error[E0658]: the `#[no_core]` attribute is an experimental feature
 --> /home/runner/work/glacier/glacier/ices/86238.rs:1:1
  |
1 | #![no_core]
  | ^^^^^^^^^^^
  |
  = note: see issue #29639 <rust-lang/rust#29639> for more information
  = help: add `#![feature(no_core)]` to the crate attributes to enable

error: failed to find an overloaded call trait for closure call
 --> /home/runner/work/glacier/glacier/ices/86238.rs:4:5
  |
4 |     one()
  |     ^^^^^
  |
  = help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have associated `call`/`call_mut`/`call_once` functions

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0658`.
==============

Co-authored-by: rustbot <rustbot@users.noreply.github.com>
@scottmcm scottmcm added the S-tracking-perma-unstable Status: The feature will stay unstable indefinitely. label Nov 10, 2021
@joshtriplett joshtriplett added S-tracking-needs-summary Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation. and removed S-tracking-perma-unstable Status: The feature will stay unstable indefinitely. labels Dec 8, 2021
@joshtriplett
Copy link
Member

Marking as "needs-summary", because we need a closer look at the use cases for #[no_core], and why it is that core can't "compile to nothing" to serve some of those use cases (macro-only crates aside).

@scottmcm
Copy link
Member

scottmcm commented Dec 8, 2021

no_core implies so many things -- no Add, no Fn, etc -- that it doesn't seem like the right way to expose whatever the goals here are. To me, no_core is perma-unstable, as it's there for internal reasons so will stick around but won't be stable.

That's not to say that there couldn't be other things that would make sense to have on stable, like a "please don't use simd" kind of flag/attribute or "I wish LTO worked better to remove parts I don't need" or ..., but those wouldn't be this issue.

@mamins1376
Copy link

In a recent embedded project, I needed to calculate CRC. I knew there is the crc crate which is quite minimal and meets every requirements.

The project is written mostly in C, so in order to save time, I exported a simple interface:

#[no_mangle]
unsafe extern "C" fn calculate_crc(data: *const u8, len: usize) -> u32 {
    ...
}

... and turned that into an object file with $ cargo rustc --release -- --emit=obj and simply linked it to the rest.

This had no dependency on core. Initially I declared the whole thing as #![no_core] but faced the mentioned missing lang items.

Does this count in any way?

@luke-biel
Copy link

no_core would defiintelly open up possibility of alternate core implementations. How useful would that be in current rust ecosystem - hard to tell. I could see this used in projects that require formal verification - eg. Ada limits it's stdlib for SPARK. But there may be better ways to achieve this.

@gheoan
Copy link
Contributor

gheoan commented Apr 25, 2022

Should I be opening bug reports related to my use of no_core? I've seen confusing diagnostic messages and ICEs.

@petrochenkov
Copy link
Contributor

@gheoan
ICEs need to be reported and fixed in most cases, even in no_core.

As for diagnostics, I don't think we should do anything for no_core-specific diagnostics, unless it's zero cost in terms of code added to rustc.

@eggyal eggyal mentioned this issue Jun 12, 2022
@eggyal
Copy link
Contributor

eggyal commented Jun 12, 2022

ICEs need to be reported and fixed in most cases, even in no_core.

Contradicts #92495 (comment):

Personally, I don't think ICEs with #![no_core] are worth addressing. It's a perma-unstable feature, and lang items are effectively internal compiler code that happens to be written in a crate. We don't try to prevent ICEs in the face of modifications to the compiler itself (in fact, many internal compiler APIs will deliberately ICE when they are used incorrectly).

The only correct way to use #![no_core] is to copy-paste all of the lang items, types, and impls that you're using (and even then, leaving out "unused" ones is pretty dubious).

@scottmcm
Copy link
Member

I think getting ICEs in no_core for not providing all the things is absolutely to be expected, and not something that needs to be fixed. For example, u32 is Copy, and if you don't include that and thus get an ICE, too bad. We should not expend any effort at all to improve that situation.

@tgross35
Copy link
Contributor

I could see this used in projects that require formal verification - eg. Ada limits it's stdlib for SPARK. But there may be better ways to achieve this.

Imho, I think that it would be good to give Rust some hooks that would allow for use in safety-critical systems that could potentially eliminate the need for a separate core (for at least the basic SIL levels) - which would probably have benefits to the language as a whole. Some things I've considered that would help that -

  • #![no_panic] being used to classify a any potential panics as UB. This would be pretty useful as a sanity check too throughout the language, #[no_panic] tags could be added to a lot of core/std functions for the compiler to verify (this would be useful in kernel space too)
  • Some way to disallow unchecked casts and arithmetic
  • Some sort of way to programatically tie unsafe blocks to the code that makes them safe. I'm stealing a bit from Ada contracts here where you can have a syntax like with Pre => Name'Length > 0 for a function definition, indicating a precondition to make that function valid. It would be pretty awesome if you could do that sort of thing for any unsafe fn, indicate a precondition and make the caller indicate the lines where they check that brecondition to be true.

The first two things are basically clippy lints, but they would need to be applied recursively to everything linked. Stack size analysis is another thing, but this is already sort of coming via stack_sizes. It would be interesting to reach out to Ferrous Systems and see what the people there have to say on the subject.

Regarding the cfg-if thing, is there any reasons the implementations aren't just exported from core?

@joshlf
Copy link
Contributor

joshlf commented Oct 2, 2022

@kotauskas

If core requires a certain crate for its convenience features and that crate is small enough to be used by core and to not depend on core at all, then why wouldn't core just include that crate into itself as an RFC to the language as a whole? If core depends on such a crate, therefore all of its members are linked to core and take the disk space in the final executable, meaning that not there's totally no reason to avoid using such a crate even if it's used only briefly in a very specific place where it's not that necessary which is why people rewrite a subset functionality from existing crates themselves for their specific use case.

IIUC you might want to have core depend on an external crate for its implementation without exposing that crate's API in core's API. E.g., per @gnzlbg, "we currently duplicate cfg-if in libcore, libstd, stdsimd, and in libc" - even if we don't intend to expose cfg-if's API anywhere in the standard library, it would still be a boon to allow libcore, libstd, stdsimd, and libc to depend on a single crate rather than duplicating code across their implementations.

@Cerber-Ursi
Copy link
Contributor

it would still be a boon to allow libcore, libstd, stdsimd, and libc to depend on a single crate rather than duplicating code across their implementations.

If I understand the argument correctly, it is essentially "except core, they all already depend on a single crate, namely core itself".

@gorentbarak
Copy link

I really don't understand why you would use this. A lot of people have given issues and said that "this isn't useful." But nobody has asked why you would even want not to use core. Is there a reason that I haven't realized? Why would I want to get rid of core, which has so many amazing features that do not depend on the operating system.

@aeldidi
Copy link

aeldidi commented Apr 3, 2024

just writing here to throw in my $0.02. I’d like to avoid libcore in some places where I’d rather not be obligated to follow the license of the libcore code for some particular binary (this seems to indicate its licensed under MIT/Apache2).

Now in 99% of cases this is a complete non-issue, but surely it should at least be possible to write a (non-trivial) rust program without needing to do so? It’s not even really documented anywhere that an empty rust program with no_std still has a dependency on an MIT/Apache2 library, which might surprise some people, I’d say.

Aside from that (little bit of pedantry) though, I figure it’s at least worth having for completeness’s sake, as crates like cfg-if have shown might be useful. If some crate doesn’t depend on libcore, it should be able to express that. Keeping it as a permanently unstable feature might be wise, but it could be just as wise to stabilize it and epoch-bump whenever the feature is broken, seeing as it’s unlikely that would happen too often.

Either way, I think this is a good thing assuming it wouldn’t be a stability nightmare or anything.

@Cerber-Ursi
Copy link
Contributor

Well, if we talk about licenses... are the compiler intrinsics distributed under MIT/Apache too?

@aeldidi
Copy link

aeldidi commented Apr 4, 2024

LLVM has a specific exception for these

---- LLVM Exceptions to the Apache 2.0 License ----

As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.

In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

(From LICENSE.txt in LLVM). Does Rust have some equivalent that I wasn't aware of? I couldn't find any such thing but that would make the licensing thing a non-issue.

@tgross35
Copy link
Contributor

tgross35 commented Apr 4, 2024

LLVM is only distributed under the Apache 2.0 license, and excluded sections 4(a), 4(b) and 4(d) exempt the parts about needing to provide the license upon redistribution and giving attribution. Rust is dual licensed under either Apache or MIT at your choice

rust/COPYRIGHT

Lines 16 to 19 in 43f4f2a

Except as otherwise noted (below and/or in individual files), Rust is
licensed under the Apache License, Version 2.0 <LICENSE-APACHE> or
<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
<LICENSE-MIT> or <http://opensource.org/licenses/MIT>, at your option.
. So de facto selecting to use core under the MIT license already means you don't have to worry about these attribution things, nor the GPL / patent provision / indemnity provision waiver.

It’s not even really documented anywhere that an empty rust program with no_std still has a dependency on an MIT/Apache2 library, which might surprise some people, I’d say.

Under what specific circumstances would this be surprising? Having the MIT license allows the source and software to be distributed liberally. Having no license is less free, unlicensed software cannot be distributed at all. LLVM intrinsics are still under the Apache2 license, just with some exceptions. Using Rust's core under MIT gets you roughly the same permissions as the modified Apache.

@aeldidi
Copy link

aeldidi commented Apr 4, 2024

So de facto selecting to use core under the MIT license already means you don't have to worry about these attribution things

This isn't correct. The MIT license definitely requires attribution and including a copy of the license with distribution.

Under what specific circumstances would this be surprising?

Under the circumstances that both gcc and clang both have exceptions so that this isn't the case. The norm for people coming from C/C++ is that "compiling without the standard library means you don't have to worry about licenses", and it's surprising that rust differs in this way.

Having no license is less free, unlicensed software cannot be distributed at all. LLVM intrinsics are still under the Apache2 license, just with some exceptions. Using Rust's core under MIT gets you roughly the same permissions as the modified Apache.

Again: the differences granted by the exception are what I'm after here. Whether or not something is licensed under Apache 2 or MIT, the point is whether or not the user has some hidden obligation to give a copy of the license and give attribution when it's not obvious. With both gcc and clang, these exceptions mean that someone compiling with -nostdlib doesn't have to worry about producing any licenses or giving attribution at all, which I was trying to say would be nice to have in rust, since It's a little odd that without no_core every rust program requires giving attribution and a copy of some license.

The proposed solution here isn't "no license is better", just that having no_core or introducing some linking exception for libcore would be nice.

@tgross35
Copy link
Contributor

tgross35 commented Apr 4, 2024

This isn't correct. The MIT license definitely requires attribution and including a copy of the license with distribution.

I am not a lawyer, but I believe the clause The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software is usually interpreted as "Software" referring to the library itself (i.e. Rust as a whole), but anything built with it is not considered a "copy" or "substantial portion".

Whether this is the correct way to interpret it or not I do not know. The question really isn't related to no_core, so could you bring this up on Zulip?

@aeldidi
Copy link

aeldidi commented Apr 4, 2024

I agree that the specific issue on what the MIT license requires in binary distribution is off topic, I'll continue that discussion elsewhere.

@LegNeato
Copy link
Contributor

LegNeato commented Apr 21, 2024

I think I have a use-case for this. I want to specify a logical data model using Rust's syntax and type system so that implementations can map in and out (a la serde). It is important to keep it zero-sized to maintain maximum flexibility for both the people mapping in and out as well as the end users relying on those crates.

I want to mark my definition crate as #[no_core] so the compiler automatically guarantees I don't accidentally depend on a specific storage implementation detail such as u64.

I can achieve my overhead and compatibility goals by sticking to const, but a) there's no guarantee that const happens at compile time and b) even if it was guaranteed to run at compile time, there is not something like #[const_only] to have the compiler make it so I don't inadvertently introduce something non-const wrapping the const in the future.

@scottmcm
Copy link
Member

I want to mark my definition crate as #[no_core] so the compiler automatically guarantees I don't accidentally depend on a specific storage implementation detail such as u64.

u64 is a magic primitive, not something from core, so it'll still be there even in no_core. (And the correct way to do type restrictions is with traits, not trying to remove them from existence.)

@scottmcm scottmcm changed the title Tracking issue for no_core stabilization Tracking issue for internal feature no_core Apr 21, 2024
@LegNeato
Copy link
Contributor

LegNeato commented Apr 21, 2024

u64 is a magic primitive, not something from core, so it'll still be there even in no_core. (And the correct way to do type restrictions is with traits, not trying to remove them from existence.)

Right, but if I make a NoCore trait I can't tell the compiler to enforce that I am not using anything from core either in the trait or for my items that impl it.

One could informally not use std and have the same observable effects (minus linked std which might be stripped), but the existence of an optional #[no_std] is a strictly better alternative as invariants are enforced by the compiler. I don't see how core is different, other than the fact that there are fewer uses of #[no_core].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC S-tracking-needs-summary Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests