text:
Create a separate libc_types crate for basic C types

Rendered

I'm not really sure I quite understand the motivation for specifically the crate split itself here. Presumably crates could just use the libc crate without actually using the functions, right? If we wanted more principled access then we could perhaps leverage scenarios to gate access to APIs in a more statically-checked fashion.
I guess put another way, I'm not particularly clear on what the downsides of using libc are today. Could you clarify in the RFC what these downsides are?
As a side note, as well the drawbacks and alternatives sections here are particularly bare, and the alternatives section doesn't seem to have a lot of thought put into it. Perhaps these sections could be fleshed out a bit more with some more information? For example "adds an additional crate to the standard library" to me is a pretty massive drawback as an incredibly large hammer. Why not crates.io? Why not part of libcore? Why not in libc itself? (for example...)

The main motivation is being able to use the C types without linking to libc. This allows them to be used in environments that cannot use libc (kernels) or don't have a libc implementation (bare metal).
As mentioned in the comment above, it seems that I didn't word the drawback correctly. Like libc this new crate would only be accessible by users from crates.io, but it would still be one of the crates distributed along with the standard library since the standard library depends on libc (although that crate would be hidden behind feature(libc)).

I would really like this to happen and my exact usecase for such a split is the -sys crates which provide bindings to some C API, but don’t want to link to liblibc, which is quite a big crate itself. I would feel much better about introducing a dependency on a small crate with standard C types only.
Currently I prefer using std::os::raw, but that precludes making the crate no_std and still introduces a transitive dependency on liblibc.

What happened to c_char?  C makes it implementation defined whether it's signed or unsigned.
c_char is in the existing liblibc, but some other C types specified by the standard aren't:

_Bool
long double
float _Complex, double _Complex, long double _Complex

@comex Ah, nice catch, I seem to have forgotten about c_char.
_Bool is currently layout-compatible with the Rust bool type, but whether we should guarantee this is the topic of a separate issue: https://github.com/rust-lang/rust/issues/14608
As for the other types, they simply aren't currently supported by Rust.

The main motivation is being able to use the C types without linking to libc. This allows them to be used in environments that cannot use libc (kernels) or don't have a libc implementation (bare metal).

I don't fully agree with the motivation behind this point, depending on what's happening. If libc is available, there's no detriment linking against it. It'll get stripped if it's not used. If libc is not available, however, then you're on a whole separate target, in which case the libc crate itself can just give you a different API (because no library exists).
That is, in my mind libc doesn't link to the actual libc for any platform that doesn't have one, so this problem wouldn't exist.

Like libc this new crate would only be accessible by users from crates.io, but it would still be one of the crates distributed along with the standard library

These are just type definitions, why would it need to be included in the standard library? Why not ship a no_std crate on crates.io that has these types?

I would really like this to happen and my exact usecase for such a split is the -sys crates which provide bindings to some C API, but don’t want to link to liblibc, which is quite a big crate itself

Technically, this is incorrect. If you have a crate that binds a C API crate, then that native library requires libc, so that dependency needs to be expressed. If the liblibc crate is "too big", then that's a problem that needs to be solved.

I think the motivation here is that the libc crate in its current form expects that your target has a full and complete version of glibc or musl available, and that is not always the case with embedded systems. Let's say your target has a toolchain based on a minimal version of libc like newlib. If you try to link the crate solely for its type definitions, you will fail at compile-time if target_os = none (as there are no typedefs for the none case). If you set target_os = linux, then libc will compile, but it will fail at link-time because it can't find librt and libutil.
The case above is one that I'm personally familiar with, and perhaps that particular case could be addressed with explicit newlib support in the libc crate.  But I wouldn't be surprised if there were other cases similar to this involving other obscure versions of libc.

These are just type definitions, why would it need to be included in the standard library?

Because the in-tree version of libc would be dependent on it.

Why not ship a no_std crate on crates.io that has these types?

That would presumably be done too, kind of like how there's an in-tree version of libc used by libstd and one on crates.io too.

I'd like to see this as well, for building core-only programs with no libc that have FFI calls between Rust and C.

Is this different from std::os::raw? Perhaps by being available to #![no_std] crates? If that’s the only reason, what about moving it?

@SimonSapin Yes, this is basically a no_std version of std::os::raw. I wouldn't mind simply moving it to core, but the consensus on the internals thread seems to favor using a separate crate instead.

Hm so I may not quite be being clear here, so let me try again! The purpose of the libc crate is to provide the types and definitions of what the underlying platform exposes exactly. That is, it theoretically contains all C types and definitions for interoperating with the underlying system. This implies that a libc_types is not necessary because it's what the libc crate is supposed to do already.
Comments have been made about how libc today "requires too many libraries" or "pulls in too many symbols". This is not an inherent limitation to libc, but rather because libc is being lied to about its platform. The libc crates has been ported to a set of platforms, and it provides absolutely no guarantee of working on any other platform. If you've got a platform that doesn't have libc/librt/libutil then libc has not yet been ported to your platform. If such a platform masquerades as x86_64-unknown-linux-gnu then it's misleading to libc as the libc API is then specifically tailored to what that platform actually is.
The intent of the libc crate design was specifically to avoid crates like libc_types. If the underlying platform in fact has no C library, then the libc crate will expose that and not give you any function delcarations or library linkage, just type declarations. This has not been implemented in libc yet, but that's just because an effort hasn't been made just yet to do so.
Does that make sense? This is what I mean by the alternative above of just emptying libc of function declarations and just having types for these sorts of platforms. For example I could imagine an alternative to this RFC along the lines of:

If cfg(target_os = "none"), then the public API of the libc crate will only be the standard C type definitions.

That is, if cfg(target_os = "none"), then the libc crate looks exactly like the libc_types crate being proposed here.

@FenrirWolf

I think the motivation here is that the libc crate in its current form expects that your target has a full and complete version of glibc or musl available, and that is not always the case with embedded systems.

It does indeed! I explained this above, but to reiterate here this is not a limitation of the libc crate. The libc crate has only been ported to platforms with libraries like glibc/musl, it just hasn't been ported to any other platforms (but it certainly can be!)

Let's say your target has a toolchain based on a minimal version of libc like newlib. If you try to link the crate solely for its type definitions, you will fail at compile-time if target_os = none (as there are missing typedefs for the none case).

These problems are all indicative of the work has not been done to port libc. That work can certainly be done, and it can be done at any time in the libc crate. Attempting to masquerade as a different platform and then seeing failures to me isn't a reason to abandon the libc crate entirely.

Why not ship a no_std crate on crates.io that has these types?

That's what he's proposing.

To clarify, this crate is being proposed to ship with the distribution, not on crates.io. Shipping a crate on crates.io doesn't require an RFC.

@alexcrichton
I would argue that the target_os is a distinct concept from whether you want your application to link to libc. On Linux for example, there are several use cases where you need very fine grained control over your address space and which syscalls are used. In such cases you would still generate a binary targeting Linux, but you would not link to glibc and would instead issue syscalls directly (using the syscall crate).
The idea of libc_types on the other hand is to expose only the type definitions needed for a freestanding C environment. These types are needed to link with other freestanding code, or for the various structure types used in system calls. libc_types provides these types without needing to link to the system libc.

To clarify, this crate is being proposed to ship with the distribution, not on crates.io. Shipping a crate on crates.io doesn't require an RFC.

Not exactly. What I am proposing here is a crate on crates.io, however it is important that libc re-exports these types instead of using its own to avoid conflicting definitions of c_void. The libc_types crate will become part of the distribution only as a side effect of being a dependency of libc, and will be mostly invisible since users will use the crates.io version.

@alexcrichton so does this mean libc will take PRs for any platform at all? Like, if I wanted to "implement" libc for intermezzOS, with just the types and none of the functions, that'd be an acceptable PR? Or would it be expected that smaller platforms (for some definition of small) should use some sort of forked libc? That's going to reintroduce the conflict, though.

@Amanieu I think @alexcrichton's proposal sounds fine if you replace cfg(target_os = "none") with cfg(target_env = "none")

@Amanieu

The idea of libc_types on the other hand is to expose only the type definitions needed for a freestanding C environment.

My point is that the purpose of libc is to be precisely what you want libc_types to be on the platforms you're thinking of. If you want to use libc in a freestanding environment, then it just needs to be ported to that environment (which involves probably only exposing types).
My idea of target_os was just an example, we could use basically anything (cargo features, target_env like @parched suggested, etc). The intention is that a "freestanding libc crate" is precisely the same thing as libc_types proposed here.

@steveklabnik

so does this mean libc will take PRs for any platform at all? Like, if I wanted to "implement" libc for intermezzOS, with just the types and none of the functions, that'd be an acceptable PR?

Indeed!

@Amanieu if you could blacklist the relevant hosted features/scenarios/whatever for libc, that would solve the problem, right?

My point is that the purpose of libc is to be precisely what you want libc_types to be on the platforms you're thinking of. If you want to use libc in a freestanding environment, then it just needs to be ported to that environment (which involves probably only exposing types).

What if you need libc to serve both sides?  You may want to have just the types for your FooOS kernel, but still have a full interface of types and functions for FooOS userspace programs.

In the case where the types are mixed into the libc crate, how should the crate author communicate that they only depend on the types, and not on the library functionality? Or should there not be any way to write that into a crate spec?

@cuviper I would assert that those are two separate targets, not one. In that sense libc would compile itself separately for both targets.
@jmesmon there shouldn't be any reason to not want functions from libc. If functions don't exist on a platform then libc was miscompiled (or not ported). If the functions do exist then there's no cost to compiling them in.

There's no reason, AFAICT, to prefix the names of the types with c_. In ring we define ring::c::{int, long, size_t} with no c_ prefix and it works fine. So, I suggest dropping the c_ prefix.

The use of these types from the libc crate should be deprecated/discouraged in favor of using them from this new crate.

In the ring project, we have tests that verify the size and alignment of the Rust alias and the C type correspond. See https://github.com/briansmith/ring/blob/master/src/c.rs and https://github.com/briansmith/ring/blob/5c4627a8499e3489a466e280bb606fbed6cc3e67/crypto/crypto.c#L80-L102. I suggest similar tests should be added. Having such tests have definitely prevented serious bugs in ring.

@jmesmon there shouldn't be any reason to not want functions from libc. If functions don't exist on a platform then libc was miscompiled (or not ported). If the functions do exist then there's no cost to compiling them in.

I disagree. It is useful to document statically in a toolchain-enforced way that no C library functions are being used by a crate, when that is the case. When looking at just the dependencies of a libc-based crate, one cannot tell whether it depends on C library functions or not. This matters for some embedding use cases.

To try to recap some of the discussion so far, there are a handful of situations that come into play. In all cases, I assume you want access to C types.

Your platform does not have a libc implementation, but you want C types. This is where there seems to be the greatest disconnect in the discussion. @alexcrichton is pointing out that for any such platform, the liblibc crate should provide exactly the C types. More generally, liblibc on a given platform should export the "standard C APIs", as per its RFC. In "freestanding" cases, that will consist of just the C types, exactly like this RFC.

Your platform has a libc implementation, but you don't want to link it. As @alexcrichton says, it'll get stripped if not used.

Your platform has a libc implementation, but you want a strong guarantee that you don't use it. In addition to this RFC, another possibility would be a mechanism like scenarios, which are useful for cutting down and providing such guarantees for many libraries. (Expect an RFC on scenarios soon, which should clarify that potential mechanism).

AFAICT, the only situation not addressable using today's setup is the last one, and we may want to consider the forthcoming scenarios RFC just as a point of comparison for that last bullet.
Finally, there's been some question about why this crate would need to be part of the standard distribution. I believe it comes down to c_void, which is not defined as a type alias; if we want compatibility with the liblibc crate version of this type, one must be a re-export of the other.

Aren't dependencies between crates already the unit that we use to communicate a "uses" relationship?
It isn't clear to me why we'd need to introduce another item just to solve this. We've already got crates and dependencies.
This issue almost feels like the same one we have with crates wanting a way to not depend on std. Because cargo doesn't know about it, all that info ends up being tracked essentially ad-hoc via cargo keywords & features.
But the great thing about the solution to the libc issues (compared to the std implicit dependency) is that we don't need a magnificent new framework to solve them. We've already got crates.

@aturon As another possibility, that doesn't require waiting for scenarios: what if we added feature flags to libc?  default could include everything for the platform, and omitting default would just get you the types.

I would encourage people here to read the internals thread in which many of the same comments have already been made. The main point of that thread is that there is one type that wholly restricts any solution. std::os::raw::c_void is in std and is stable. There is absolutely no good reason that libc::c_void and std::os::raw::c_void are different types and this is very confusing. The only way to make them the same type is if they're actually from the same crate. However, libc shouldn't depend on std. The only way to solve this is to define a c_void type somewhere in a stable crate shipped with the compiler. This basically leaves two solutions:

a ctypes crate shipped with the compiler that both std::os::raw and libc re-export.
put c_void in core and re-export that in std::os::raw and libc.

/end summary
This choice needs to be made and will inform the rest of our solution. While not strictly necessary, I think it would be a good idea to put all the other c_* types in the same crate (they're just type definitions).
Some discussion in this thread has been about whether libc exists or not on your platform. I don't think this is relevant to the question. The C types are part of the C ABI, the most widely used ABI on most platforms. Basically if you're calling extern "C" code from any source, you're likely to need the C types. This is exactly why they should be defined in a separate crate.

I think with std-aware cargo, we could have std depend on the public liblibc.

@jethrogb
Thanks for the reminder about that issue -- while I'd mentioned c_void in general, I'd forgotten about the incompatibility between the current std and libc versions of that type!

This RFC should be merged as is

It would be good for the RFC to make any mention of the issue you brought up, since in my mind that is actually the most compelling motivation for making a change along these lines. @Amanieu, could you please update it?

@alexcrichton, to bring the previous discussion back into cache, in my most recent post I had basically outlined precisely what this RFC does, which most people on thread seemed roughly on board with. However, you had a couple of concerns (see your replies to that comment), which I'm now having a bit of a hard time grasping.
Would you mind laying out your worries in more detail in this thread? What do you currently see as the best way to solve the c_void problem across std and libc, and in particular, do you see a better route than this RFC?

@joshtriplett Yes, Cargo features in libc would suffice for what I was suggesting. But @jethrogb has re-raised an important issue that this RFC could solve, which wasn't previously mentioned in the RFC or this thread.

@jethrogb Actually, one further note about the current RFC text: it doesn't mention anything about the relationship to the current types in std. If we want to solve the problem of that internals thread, we need to make some change there.

@aturon Seems like the simplest solution, then, might be to add the remaining c_* types to core (and move c_void from std to core, re-exporting it in std).

@joshtriplett I agree. In the previous discussion, I tried to tease out why people were resistant to this idea. See, for example, @alexcrichton's comment for some details.

@aturon
Based on that comment (which @Amanieu referred to earlier in this thread ) it seems like a separate crate which is part of rustc (ie: what this current RFC proposes) is a very reasonable solution to allow crate authors to make their dependencies more explicit (the comment you've linked to even notes that such a crate might be created in the future).
As we've established that adding another set of targets to libc that lack function definitions fails to solve this, what reason is there to avoid having a separate crate for C types?

Hmm, actually there is still a problem with c_void in a ctypes crate. The problem is that, when compiled, there will be 2 versions of the ctypes crate: one from the standard distribution, which will be unstable and hidden behind a feature flag, and one from crates.io which will be used by applications. The problem is that std::os::raw::c_void will re-export the c_void from the internal ctypes crate, while libc::c_void will re-export it from the crates.io ctypes crate. This means that we still end up with two incompatible definitions.

c_void in core seems like a pollution of core---though a fairly harmless one solving a worse problem. But with what @Amanieu says, that doesn't solve the problem anyways. Getting around that problem would also make my crates.io libc == internal libc solution possible, which avoids the core pollution.

The problem is that, when compiled, there will be 2 versions of the ctypes crate: one from the standard distribution, which will be unstable and hidden behind a feature flag, and one from crates.io which will be used by applications.

There will not be a version from crates.io. When fully stabilized, people would write extern crate ctypes (or whatever we end up choosing) and it would work just like when they would write extern crate core or extern crate std today.

If you're proposing a different solution, please describe how it solves the above problem.

@jethrogb My alternative solution in winapi is to define c_void myself in winapi but provide an opt in feature to make it instead re-export the version from std.

Can't we just expose c_void somewhere in libcore?

Can't we just expose c_void somewhere in libcore?

+1
I previously proposed to move all of std::os::raw to somewhere in libcore, but there was resistance to having OS/platform-dependent stuff in libcore.
Moving just c_void doesn’t have this problem, and it solves some issues because it is the only item of std::os::raw that is not a type alias. (It is an enum.) Identical type aliases defined in separate crates are compatible (they all refer to the same unaliased type), but identical enums are not. This was an important part of what made the ecosystem’s move from libc 0.1 to 0.2 painful. (Two libraries using *mut c_void in their public API could be used together until they started depending on and importing c_void from different versions of libc.)

Thanks for the summary comment @aturon, I think definitely helps keep us on track with the state of play here.
For the first two points of your summary:

Your platform does not have a libc implementation, but you want C types
Your platform has a libc implementation, but you don't want to link it

I personally still feel these do not motivate another crate. The design of libc today solves both of these constraints. So to the other points of the summary:

Your platform has a libc implementation, but you want a strong guarantee that you don't use it

This is a relatively recent concern, but a good one! I know I personally like to be very principled about how libc is used. That being said, I still feel that it does concern doesn't motivate another crate. As you mentioned this fits basically perfectly with our idea of scenarios, and in the interim I think @joshtriplett has a good idea of just adding an on-by-default Cargo feature.
And now that brings us to another new scenario that's popped up during discussion:

There should be one definition of c_void when sharing the type

The situation has been quite extensively discussed. The RFC also does not currently mention this as a motivation. If we would like to start discussing this point as a motivational point for the RFC, I would highly recommend reading that internals thread to ensure we don't just relititgate the same points here again.
@aturon points out that the most recent proposal on that thread is very similar to this RFC. To summarize that proposal here again, it says to:

Move c_void's definition to libcore, the proposal does not say precisely where inside libcore.
Add a ctypes crate to the distribution. Note that this current RFC is explicitly not proposing this.
Move all of std::os::raw to ctypes, have ctypes reexport libcore's c_void, and have libstd reexport all of ctypes in std::os::raw. The exact structure of ctypes is not specified yet.
Update libc to optionally reexport these types. Compiler detection and/or an opt-in feature is required to avoid a major version bump to libc.

I had early concerns with a ctypes such as C compilers changing the meaning of these types along with compatibility hazards like the long definitions. I don't feel like these were ever addressed when considering a ctypes crate. My later concerns were basically that we can't do anything until ctypes is stable, but in retrospect that makes sense. My final concern with this solution is that this seems like an incredibly massive hammer for such a relatively tiny problem (in my opinion at least).
If we'd like move the discussion of c_void onto this RFC, however, then an idea I had recently was to just change the definition of c_void in the first place to:
pub type c_void = !;

This type is defined by the language, so it's automatically shared and it doesn't matter where your definition of c_void the name comes from. It's also actually a "correct" definition in the sense that you cannot create an instance of c_void, and it also sidesteps I believe every previous problem mentioned with the definition of c_void. Furthermore I believe this is a backwards-compatible change to make as soon as !-as-a-type is stabilized. Even better the feature is implemented and it seems like we're in the last stretch before stabilization.
@aturon does that answer the questions you raised here?
Do others agree that this would solve the c_void problem?
Do others agree that if c_void is not an issue that ctypes is not needed?

@alexcrichton

Do others agree that this would solve the c_void problem?

There is one problem with your proposed definition of c_void: it is currently implemented as an enum with repr(u8), to match the LLVM IR that clang produces for void * in C. LLVM relies on this to recognize function prototypes for built-in functions like memcpy. Therefore we would have to ensure that pointers to ! are translated to i8* in LLVM IR.

Do others agree that if c_void is not an issue that ctypes is not needed?

Apart from that issue, you are correct. A ctypes crate could then simply be provided on crates.io without any special treatment.

To me LLVM's current behavior is not a justification for forever adding a ctypes crate to a distribution. Something like that needs to be motivated in its own right. We can likely more than easily either still match LLVM's codegen somehow or update LLVM to work with us like it does with C.

Conceptually, C's void and !, which is the Void type, are different. This is actually explained in the the RFC: "Although Void has some precedence in languages like Haskell and Idris the name is likely to trip up people coming from a C/Java et al. background as Void is not void but it can be easy to confuse the two." That was a significant part of the justification for giving the void type the terrible name ! instead of the name Void as in other languages with reasonable type systems.
Given x: *const !, the only valid value for x is NULL. Similarly, given x: &const !, there is no value of x that is possible. Thus the compiler can optimize the code on those assumptions. My understanding is that this is a key to supporting ! in generic code in a reasonable way (particularly in terms of dead code elimination). This is why people are discouraged from using enum Foo {} to represent C opaque structs that cannot be instantiated in Rust.

@briansmith I agree what *const c_void exists today, but the compiler can't assume that *const ! can only contain the value NULL. Any *mut T can be any old bag of bits like a usize. So in that sense with raw pointers (the vast majority of usage of c_void) there's no problem as e as *const ! is a valid expression.
That's true, though, that &c_void would be a problem. Does that ever come up, though? In theory that's unsafe as well as we wanted to have c_void be an empty enum regardless. I would consider any existing uses of &c_void, if any, as unsafe and required to change.
I'm also not personally super concerned about the actual difference between void and ! in terms of what it means in the theoretical in sense. For all intents and purposes c_void is an empty enum, and defining it as ! is just reflecting that.

why people are discouraged from using enum Foo {} to represent C opaque structs that cannot be instantiated in Rust.

I'm confused by this because I use this pattern extensively and I wouldn't consider this being discouraged at all.

@briansmith I agree what *const c_void exists today, but the compiler can't assume that *const ! can only contain the value NULL. Any *mut T can be any old bag of bits like a usize.

I think you know better than me, but I'm not convinced that that is true. Where in the Rust reference does it say that a pointer is allowed to have a value that isn't the address of a valid value? I think it might not be decided yet, and I'd suggest it is good to define *const T to be isomorphic to Option<&T> and *mut T to be isomorphic to Option<&mut T>. To allow for that, one must assume that invalid pointer values are not allowed. (Note that we already have usize which can represent a pointer-like value that may not point to a valid object.)

That's true, though, that &c_void would be a problem. Does that ever come up, though? In theory that's unsafe as well as we wanted to have c_void be an empty enum regardless. I would consider any existing uses of &c_void, if any, as unsafe and required to change.

I agree that &c_void should probably be discouraged. however if c_void is defined as ! then how would use prevent/discourage people from using &c_void? In particular, I think the compiler needs to support &! to mean "a reference that can't exist" for generic code.

I'm also not personally super concerned about the actual difference between void and ! in terms of what it means in the theoretical in sense. For all intents and purposes c_void is an empty enum, and defining it as ! is just reflecting that.

As you can see from the RFC that added ! as a type, it was agreed to make a distinction. C uses the term void to mean two different things: void * is "pointer to unknown type" and non-pointer type void is the empty type. C's void * is Rust's *mut c_void and C's void is Rust's !.

why people are discouraged from using enum Foo {} to represent C opaque structs that cannot be instantiated in Rust.
I'm confused by this because I use this pattern extensively and I wouldn't consider this being discouraged at all.

I have been using that pattern extensively as well. However, multiple people pointed out to me that it is bad because &Foo is a reference of a type that has no values so there can be no value of type &Foo, or at least the compiler is allowed to assume that is the case.
For example, one can write extern { fn BIGNUM_add(r: &mut BIGNUM, a: &BIGNUM, b: &BIGNUM); } to indicate that NULL isn't accepted for any of r, a, or b, and that r doesn't alias either a or b, but a and b may alias each other. This is good but it doesn't work for &T if T is an empty type, because the compiler is allowed to assume that it isn't possible to construct a reference to a value of such type.

@briansmith well... I don't really want to derail this discussion too much, and I fear this may be getting into the weeds a bit. It's possible to substitute what I was thinking also with something like
type c_void = u8;

The crux just being that we use a preexisting language type to define c_void to sidestep problems of naming and such. That may raise a number of questions about references, pointers, etc, but I don't want to detract too much from this RFC. If we decide to take that route I wouldn't mind writing an RFC for changing the standard library's and libc's c_void type where we can hash out all these details, but I'm hoping to gauge the temperature a bit first to see if it's even possible to consider a strategy like this (using a preexisting language type for c_void).

Where in the Rust reference does it say that a pointer is allowed to have a value that isn't the address of a valid value?

You can cast a usize to a *const T or *mut T in safe Rust. Therefore raw pointers are allowed to point to anything, including invalid garbage. The restriction on pointers pointing to valid things only applies to &T and &mut T, so having a &c_void is clearly a bad idea and I hope nobody does this. But *const ! is totally legal and fine.

@briansmith
In any case, with the current definition of c_void, &c_void is just as valid as &!. Enums require their discriminant to always contain a valid value.

@alexcrichton I do agree with the idea of defining c_void to be an alias of a built-in type works. My point above is that ! probably can't be that type, and in fact there may not be an existing built-in primitive type that works, though [u8], i.e. *const c_void ≡ *const [u8]and*mut c_void≡*mut [u8]` might work.

You can cast a usize to a *const T or *mut T in safe Rust. Therefore raw pointers are allowed to point to anything, including invalid garbage. The restriction on pointers pointing to valid things only applies to &T and &mut T, so having a &c_void is clearly a bad idea and I hope nobody does this. But *const ! is totally legal and fine.

Please point to the part of the language definition that says so.

type c_void = u8;

This would match how gcc (as an extension) treats void, which might be nice.
https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html

My point above is that ! probably can't be that type, and in fact there may not be an existing built-in primitive type that works, though [u8], i.e. *const c_void ≡ *const [u8] and *mut c_void ≡ *mut [u8] might work.

@briansmith yeah these seem like plausible routes as well. We may have to finagle something with fat pointers and such, but I think we can work something out.

Please point to the part of the language definition that says so.

Note that we don't really have a fully fleshed out language definition right now, so what's actually written down is far from complete and/or may contain inaccuracies. In that sense there's unlikely to be a document anywhere which claims that *const ! can have any valid bit pattern or that it can't. Additionally, absence of such documentation doesn't really imply the answer one way or another either.

So if we do want to pursue this, I think we could consider options like:
type c_void = !;
type c_void = u8;
type c_void = ();

I think that the first, !, is the most backwards compatible. The u8 and () approaches are strictly speaking not backwards compatible because of something like:
impl MyTrait for c_void {}
impl MyTrait for () {}
impl MyTrait for u8 {}

That is, coherence allows that today, but if we changed the alias of c_void it would break. Now this may not ever happen, so we may not have to worry either way.
So if we decide to solve the c_void problem with the "use an existing language type" strategy, we may have a number of options.

Note that if we change the size of c_void, this will break any code that uses .offset on a c_void pointer.

@Amanieu that's true, yes, although I can't fathom how code could be correct today by calling .offset on a c_void, so I would consider that in the category of breakage I outlined above with traits: technically but practically irrelevant.

Note that we don't really have a fully fleshed out language definition right now, so what's actually written down is far from complete and/or may contain inaccuracies. In that sense there's unlikely to be a document anywhere which claims that *const ! can have any valid bit pattern or that it can't. Additionally, absence of such documentation doesn't really imply the answer one way or another either.

Agreed. That's what I was trying to say.
Why not make c_void a built-in type? There is actually no built-in type that represents it accurately.
Alternatively, why not define an type core::Unknown = ! temporarily. Then people who need a c_void can define type c_void = core::Unknown;, the actual definition of core::Unknown can change over time depending on how the language specification goes (e.g. based on what is ultimately decided that the compiler is allowed to assume for &! and *const ! and *mut !), and libcore isn't littered with a C-specific type (at least, not obviously so), and it is easy to implement.

type c_void = u8;

This would match how gcc (as an extension) treats void, which might be nice.
https://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html

Just a clarification: Based on the above documentation, GCC adds pointer addition for pointers-to-void that works like pointers-to-char, but that's much different than defining void to be an alias of unsigned char. See https://godbolt.org/g/a1G7D8 for an example of what I mean. Also, IIUC, Rust's u8 is an alias for C's uint8_t but C's uint8_t isn't necessarily an alias for its unsigned char.

but that's much different than defining void to be an alias of unsigned char.

True.  We could newtype it on u8, if we wanted the arithmetic behavior without an actual alias, and this would also dodge the MyTrait for u8 coherence problem.  Or a distinct builtin void could act like this too.

So C's void and void* aren't actually very related—the later is a pointer to an untyped object. I think Rust is actually in a better position than many languages to define "untyped object" part directly [something I've said elsewhere, not to be a broken record.] I propose a "truly-unsized" type whose size is not known at compile time or run time. This sort of construction works equally well for C's abstract structs.

Note that the crux of what I'm thinking is to not introduce a type into libcore, either as an empty enum, a type alias, etc. While that would solve the problem of c_void, I consider it equivalent to just adding c_void itself to libcore. In my mind there's not reason to change libcore as libcore itself does not want nor need the type. The real issue at hand here is there need to be a share definition of it, and in my mind it'd be great if we could piggy-back off the language.
A c_int is an i32 on 64-bit Linux, and no matter what crate defines that the type can be shared all over the place. (note that all crates must agree on the definition of c_int as there's only one "correct" answer). Similarly if we could have a c_void be "insert language defined type here" on 64-bit Linux then it wouldn't matter who defines it as long as everyone agrees. Arguably like c_int there'd only be one "correct" definition of c_void so disagreement would be a bug.
I personally feel like ! fits the bill perfectly, but that may be up for debate. I also feel that this is way to small an issue to change libcore, much less the language.

What if libc just conditionalizes its c_void based on not(feature = "use_std")? Otherwise just re-export std's c_void (which itself came from the internal libc).  So at least a use_std libc would have no conflict anymore.

Look at this interesting PR: https://github.com/rust-lang-nursery/compiler-builtins/pull/129
compiler_builtins introduces "C types" to handle platform differences.
Maybe it should depend on libc instead, like people arguing against this RFC suggest?

@cuviper I believe that was discussed briefly a few times on the original thread, my main concern (unfortunately cryptic in retrospect) is that libc 1.0 will not link to libstd by default, and in general libc shouldn't be linking to libstd, which precludes the possibility of reexporting something. That, and it doesn't really solve the problem, unfortunately, as you can still attempt to share std's c_void with libc's

in general libc shouldn't be linking to libstd

But libc can depend on std (at least the crates.io version can), and if the user is using std anyway, then why not let them opt in to having libc also use std so it can re-export that definition of c_void?

That, and it doesn't really solve the problem, unfortunately, as you can still attempt to share std's c_void with libc's

libc re-exporting std's c_void means that the two c_voids are equivalent. I fail to see how that doesn't solve the problem.

The libs team spent some time today discussing this RFC, to try to get a better collective understanding of the issues and preferences.
There seem to be roughly three problems that have been raised:

The desire for easy access to C types on platforms without libc; this is the stated motivation in the RFC.
The desire for static assurance that you're not using C functions, even if you could.
The desire for libstd and liblibc to agree on c_void.

Desire #1 seems to already be addressed in the design of libc: the library is intended to be ported to all platforms Rust supports, and to export at least C types for those platforms, in addition to whatever C functions make sense. So in principle, all that's needed for the stated motivation of this RFC is to carry out those ports.
Desire #2 could be addressed through a default Cargo feature for liblibc, and everyone on the libs team was OK with that direction. (Note, though, that due to the "union" semantics of Cargo features, if any crate in your graph turns on the feature, C APIs will be available to you. The forthcoming scenarios proposal addresses that problem.)
Desire #3 has a number of potential solutions:

Export C types in libcore, e.g. in core::ffi::c or some such.

Many people on the libs team feel that this violates the principle that libcore should be purely about Rust. (I personally would be just fine with this solution.)
There's also the issue that there is some desire for the type definitions to vary depending on the precise system setup, which might require std-aware Cargo to get fully working. But this is a solvable problem.

Export C types in a ctypes crate, as proposed.

The crate would have to become a stable, exported crate in the distribution.
There didn't seem to be a strong technical/practical argument against this approach, other than it being a somewhat big step to take to solve this issue (we have very few stable crates in the distribution).

Define c_void as a type alias to some built-in type, i.e. u8, () or !.

It's not entirely clear which of these options would work. The ! choice would likely delay solving the problem until ! is stable. And even when stable, liblibc would have to be updated to require that version of Rust, which is not ideal.

Define c_void as a type alias for some new type in libcore, e.g. core::Unknown

Updating liblibc to use this new definition would make it depend on a newer version of Rust.

OK, the above was the summary of the libs team discussion. I'll finish out with my own opinion.
I'm most interested in solving the c_void problem. To my mind, there are three plausible routes to doing so in the near future:

Adding a stable ctypes crate.
Exporting C types in libcore.
Redefining c_void to be a current stable type like u8 or ().

I'm personally fine with any of these solutions (modulo being slightly unsure about the ramifications of redefining c_void). The first two seem like the safest bet. And, to me personally, the arguments against them are not too compelling (they're mostly about a notion of purity or footprint, rather than any practical outcome AFAICT).

Oops, I missed another potential solution to desire (3), which is having liblibc re-export std's definition of c_void, as discussed above.
It's worth noting that compatibility with std is only an issue if you're using std, which is the precise case where you'd get compatible types. So offhand, to me, that seems like a reasonable approach.
@alexcrichton, I think you were somewhat concerned about the weirdness of a type changing definition based on a Cargo feature, but I'm not sure if there's a practical problem there; can you elaborate?
Like @retep998, I also can't quite understand the other concern listed in the comment.

What I don't personally understand is why a set of workarounds involving lots of "if"s and "when"s (port libc to all platforms, new cargo features, scenarios) are preferable to simply adding a new crate.
So far people are actively collecting arguments in favor of this RFC (and that's good!) but at the same time arguments against it was weak at best and @alexcrichton's  "I personally still feel these do not motivate another crate" argument carry significant weight only because he's the side with decision making power.
Why adding a crate is such a big deal?

@petrochenkov How does libc_types not have exactly the same problems as libc with respect to being ported to new platforms?

Exporting c_void from either core or a new ctypes crate both also imply libc requiring a newer Rust.  I think the only options to avoid that are re-exporting from std or using an existing type like u8 or ().

Exporting c_void from either core or a new ctypes crate both also imply libc requiring a newer Rust. I think the only options to avoid that are re-exporting from std or using an existing type like u8 or ().

This can be solved by making the definition of c_void depend on the current rust version (obtained through build.rs). On older versions of Rust, c_void would remain as an enum defined in libc, but on newer rust versions it would re-export ctypes::c_void (or core::ffi::c_void, !, etc).

@jmesmon

It isn't clear to me why we'd need to introduce another item just to solve this. We've already got crates and dependencies.

Well, strictly speaking we have crates + dependencies + features. In any case, we've found that trying to break out all possible subsets of functionality that one might want to target into distinct crates doesn't scale particularly well. As a simple example, consider things like AtomicU32 -- should we have a distinct crate or module for every atomic type, since some platforms do not support an AtomicU32? Roughly speaking, scenarios instead proposes that we put them all into std::sync::atomic and use cfg flags to only export the ones available for your current target. If you prefer to avoid some aspects of your target for greater portability, then you can specify that using a scenario, and you will get warnings (and the default is to ensure portability amongst a mainstream set of platforms). For more info see the scenarios thread.

@petrochenkov

So far people are actively collecting arguments in favor of this RFC (and that's good!) but at the same time arguments against it was weak at best and @alexcrichton's "I personally still feel these do not motivate another crate" argument carries significant weight only because he's the side with decision making power.

Hmm, I don't think this is a fair summary. I am just a random visitor to this thread, but my take on it is that @alexcrichton is basically applying the "scenarios" approach to the problem at hand. That is, libc is intended to reflect the standard "C stuff" from a single common place, and we can use other mechanisms to control how much of that "C stuff" a particular target wants to export (or app wants to employ). This seems like a princpled attempt to maintain coherent organization to me, not a matter of personal feelings.

@joshtripplet

what if we added feature flags to libc?

This seems in line with what @alexcrichton was suggesting to me. In fact, he wrote (emphasis mine):

My idea of target_os was just an example, we could use basically anything (cargo features, target_env like @parched suggested, etc).

@sfackler

How does libc_types not have exactly the same problems as libc with respect to being ported to new platforms?

libc is structured using a platform hierarchy and it's open question how to integrate "all platforms" into this hierarchy. A recent attempt to do this for libstd was recently rejected and the author was suggested to wait for the coming of scenarios.
Basic C types have very few variations and they can all be encoded once and for long time without creating platform hierarchies.

@nikomatsakis
One argument against addition of a new crate I can imagine is that ctypes may require some CI very similar to the one for libc + maintenance of crates.io copy of the crate. CI can probably be shared with libc, but it still can create some extra work for Alex specifically.
It's internal implementation detail not affecting user interface, so I still don't think it should affect the decision (unless it's especially hacky and unimplementable).
I consider ctypes more an extension to language support for FFI rather than a part of C library.
Variadic FFI functions, for example, require promotion to c_int without any libc library, but we can't name it without having at least ctypes.
Personally, I'd use ctypes instead of libc for all FFI-wrapper crates just for clarity, even if libc will end up linked into the final binary for some other reason. (Unless they are crates specific for some OS and are passing around file descriptors of something).

One more potential drawback is that it's necessary to draw the line between C types accepted into ctypes and others. For example, should FILE be in ctypes? The RFC text, however, covers this question well.

libc's structure is entirely an implementation detail - it can change to whatever's convenient. If the basic libc types can be encoded in a simpler way than the current hierarchy, then stick them in a separate submodule that follows that approach!

@petrochenkov

Look at this interesting PR: https://github.com/rust-lang-nursery/compiler-builtins/pull/129
compiler_builtins introduces "C types" to handle platform differences.
Maybe it should depend on libc instead, like people arguing against this RFC suggest?

So far people are actively collecting arguments in favor of this RFC (and that's good!) but at the same time arguments against it was weak at best and @alexcrichton's  "I personally still feel these do not motivate another crate" argument carries significant weight only because he's the side with decision making power.

Meta: comments employing cutting sarcasm or charges of bad faith are actively harmful to constructive, respectful discussion.
The Rust teams care deeply about good faith debate, and we try to follow a process of clearly laying out the design space, with pros/cons on all sides, before making an evaluation. This is always a messy process -- the motivation for this RFC has evolved significantly within the comment thread (though the text has not) -- but anyone is welcome to raise new arguments or question existing ones at any time.
As to this particular issue: I'm not sure whether you read the entirety of my comment summarizing the libs team meeting, but we specifically discussed the drawbacks of adding a new crate, and I wrote:

There didn't seem to be a strong technical/practical argument against this approach, other than it being a somewhat big step to take to solve this issue (we have very few stable crates in the distribution).

I also gave my personal opinion about the argument against adding a crate or adding types to libcore:

And, to me personally, the arguments against them are not too compelling (they're mostly about a notion of purity or footprint, rather than any practical outcome AFAICT).

Going forward, if you feel like an aspect of the debate is getting insufficient attention ("Why adding a crate is such a big deal?"), I'd encourage raising that question in a straightforward, information-seeking way.

@nikomatsakis

In any case, we've found that trying to break out all possible subsets of functionality that one might want to target into distinct crates doesn't scale particularly well. As a simple example, consider things like AtomicU32 -- should we have a distinct crate or module for every atomic type, since some platforms do not support an AtomicU32? Roughly speaking, scenarios instead proposes that we put them all into std::sync::atomic and use cfg flags to only export the ones available for your current target. If you prefer to avoid some aspects of your target for greater portability, then you can specify that using a scenario, and you will get warnings (and the default is to ensure portability amongst a mainstream set of platforms). For more info see the scenarios thread.

I suppose what I'm not seeing is why scenarios has applicability to the issue at hand regarding a ctypes crate. It sounds like that could be something that would be used by crates that have to deal with target differences, but it isn't something we need to accurately express a need for "just the C types, no library".

I would agree that depending on libc for something as basic as C's int type is actively counterintuitive. The basic primitive types are necessary for access to libc, but not the other way around. A rust program wanting to do something as simple as interface with the C function int sum(int a, int b) { return a + b; } has no need for something that purports to represent a library that may or may not actually end up existing on the target platform.
We can solve a huge headache for embedded developers by affording a tiny, self-contained, uniform representation of these standard types — without, as has been mentioned, relying on the nebulous porting status of the libc crate.

@archshift I would argue that one shouldn't have to rely on libc, but also, that one shouldn't have to rely on any crates at all. That the libc types aren't in core is mindbogglingly weird.

@archshift I would argue that one shouldn't have to rely on libc, but also, that one shouldn't have to rely on any crates at all. That the libc types aren't in core is mindbogglingly weird.

I think a large part of the problem is thinking about them as "C" types and not "ABI" types. Imagine an operating system not written in C (it shouldn't be hard now). "C" types and "libc" are likely to not be relevant to such a system. But types like uint32_t, "short integer," and "long integer" would still be.

"C" types and "libc" are likely to not be relevant to such a system. But types like uint32_t, "short integer," and "long integer" would still be.

Would they be, though?  uint32_t already exists as u32, and similar.  "Short integer" and "long integer" are fairly meaningless - how short, how long?  Are they the ideal integer type for something; if so, what?  Once long was commonly used for sizes and casted pointers; now there are size_t and uintptr_t for that.  Today long basically means "pointer-size except on 64-bit Windows", which is a useless concept except when interacting with legacy APIs and C code.  char and short are almost always (u)int8_t and int16_t, except on weird DSPs and such where they're not.  If you just want 8 or 16 bits, using those is likely to break, while if you really are prepared for size variation and want 'the fastest integer type that's at least 8 bits' or 'the smallest', better these days to be explicit with uint_fast8_t  or uint_least8_t.  Perhaps those families of types would be useful in Rust - except the C versions are made less useful in practice by legacy ABI requirements fixing them to suboptimal sizes, so it would be better to have a separate set of definitions for Rust, keeping the C versions for FFI.
In short, I don't think there's any good reason to use any of these types in new code.  It's true, though, that they're not strictly C types...

@briansmith Agreed. They do depend on the target ABI (for instance, x86-32, x32, and x86-64 would each have different sizes for certain types), but they don't depend on libc, and they have useful applications without linking to libc in any way. For instance, consider a bare-metal program (firmware, bootloader, OS, etc) that wants to load modules. It doesn't use libc, but it needs the ABI-compatible types.

uint32_t already exists as u32

Right. That was a bad example on my part.

"Short integer" and "long integer" are fairly meaningless - how short, how long?

They are defined by the operating system.

Today long basically means "pointer-size except on 64-bit Windows

Windows is a good example of a platform that defines such types independently of libc: USHORT, INT, ULONG, etc.

If the operating system is defining them independently of a libc, then why are libc's definitions relevant?

@sfackler The basic C types of int long etc are still relevant, which is why winapi provides them itself.

Yeah, they're absolutely relevant to Windows.
I really don't understand this distinction between "C" types and "ABI" types; the ABI we're talking about is the C ABI! Your custom non-C operating system may (or may not) have some concept of "short" and "long" integers, but I have no idea why they would be related to what some other implementation of the C standard defines its integers as.

So the argument that C type definitions are a separate concept than libc makes some amount of sense to me, but in that case this crate should not be named libc_types, right?

That's a good point, but I'm not sure if it's bikeshed time yet? Note that on some platforms this default ABI is actually generally called the C ABI (but not always...)
I like core::ffi if they're going to live in core. If they're going in a new crate:

abitypes
ffitypes

Ok, to re-summarize the current state of things:

There are two main motivations for this change in the RFC:

You're in a freestanding environment where there is no libc to link to.
You're in a hosted environment but want to avoid linking to libc anyway.

Neither motivation is entirely correct. The libc crate can be ported to freestanding targets without issue (the name libc may be a bit of a misnomer), and the linker will not actually crate a dependency on the system libc if it is unused.

Even if you won't end up with a dependency on the system libc, the approach of "just don't call anything" is a bit fragile compared to those function declarations not existing at all.

The c_void type is problematic as there are multiple options as to what it could be defined as, and the current choice has compatibility issues across crates. Possible approaches here are defining it as ! or u8. ! may seem a bit more "correct", though that imposes some serious migration problems as that type is unstable.

Finally, there's the idea that these type definitions are "more" than just C types - they define part of the system ABI.

Hopefully that seems mostly reasonable.
Since it seems like the discussion has moved pretty far away from the motivations outlined in the RFC text, I think it'd be helpful if the RFC were updated to reflect the current state of things.
Some questions I have:

Do people feel that it would be easier to port this new libc_types crate to new, freestanding platforms compared to libc? If so, why?
How common is the desire to write libraries that don't link to a libc in a hosted environment but do interact with C?
What is the concrete cost to using the libc crate if you want C types but don't need the rest of its functionality, and aren't in either of the above situations?
Am I correct in thinking that the c_void problem is basically orthogonal to this RFC?

How common is the desire to write libraries that don't link to a libc in a hosted environment but do interact with C?

I'm not sure how you want to measure commonality, but, depending on your definition of hosted, I'm planning on doing this in production.

Do people feel that it would be easier to port this new libc_types crate to new, freestanding platforms compared to libc? If so, why?
What is the concrete cost to using the libc crate if you want C types but don't need the rest of its functionality, and aren't in either of the above situations?

It seems much more practical to ship a small .rs file with some type definitions with your target .json than all of the libc crate. Also, the type and function definitions are somewhat orthogonal: lots of libc functions will be written the same once the type definitions are taken care of.

Am I correct in thinking that the c_void problem is basically orthogonal to this RFC?

I don't think so. Unless we can actually solve c_void by redefining it as a built-in type, the solution for that will likely advise the solution for this RFC.

depending on your definition of hosted, I'm planning on doing this in production.

The C standard is fairly explicit about its definition of hosted - as noted in the RFC text: https://github.com/rust-lang/rfcs/pull/1783/files#diff-89b88fe019d84fb3b01131b983c27532R18.

It seems much more practical to ship a small .rs file with some type definitions with your target .json than all of the libc crate.

What are your practical concerns? Is this going to be a libc_types crate that you pull from crates.io or a random .rs file that you pull into each project?

Also, the type and function definitions are somewhat orthogonal: lots of libc functions will be written the same once the type definitions are taken care of.

What does this have to do with sticking those type definitions in a separate crate? Take libc_types, plop it in libc as a module, reexport at the crate root, done.

The format @archshift is referring to is described in [http://llvm.org/docs/LangRef.html#data-layout]. I don't think the relevant information is included.

@jethrogb never mind, I deleted the comment. It was wrong.
Looking at how Clang does it, each target's type sizes seem to be hardcoded. If we want to make Rust easy to pick up for any embedded platform, though, we might want to consider a more accessible approach to defining these types. Perhaps a new field in the target specification file?

The argument against adding C types to core to keep it pure doesn't seem very strong to me in this case: Rust already includes extern "C" in the language. In the standard library, there are the CString and CStr structs and ways to get the OS handles from wrappers (like File::as_raw_handle). There has also been mention of possibly being able to #include C header files in the future (which I'd probably be in favor of) and would be a language feature. I think including all this support is just recognizing that C is a very widely used language and making it easier to work with.
It seems weird to me to have so much support for C interop in the language and standard library, but not even include definitions for a C int, long, etc. without depending on the libc crate from crates.io. I have never called a C standard library function from Rust (and I doubt I ever will), but I often need definitions for C types. You could have a C# or Java program call extern "C" Rust functions with no C code involved, but you would likely need the C types for the ABI. It doesn't make sense to me to have to depend on the libc crate for such a situation. I agree with @joshtriplett that core::ffi and std::ffi would be a good place for the types.

extern "C" must to be part of the language. CStr and CString however are just part of std. I rather pollute lower layers as little as possible---I think this is a consistent approach.

I don't think it makes sense to have FFI helper types like CStr, CString, or c_ulong in std but not in core.  They have many useful applications with no_std.

@joshtriplett I would rather keep CStr out of core until we can make &Cstr == *const c_char

Also, consider that these C integer types are a portability hazard due to overflows. By putting them in any crate but core, one can staticly check they aren't used (override the crate with something that won't build).

@Ericson2314 I don't see why they create a bigger overflow problem than usize or u32 do.

The fixed size ones overflow in portable ways—same behavior every where. *size doesn't, but that I'd argue is a necessary evil. C's strike me as less necessary evil.

@Ericson2314

The fixed size ones overflow in portable ways—same behavior every where. *size doesn't, but that I'd argue is a necessary evil. C's strike me as less necessary evil.

I don't really follow this logic. I feel like the concept of a system ABI is fairly fundamental, and I don't see how exposing said types really presents a greater portability hazard. I'm not convinced that the fact that they are isolated in another crate makes a successful static check any more likely -- the crate seems highly likely to appear in the dep-graph for any non-trivial system anyway.

I don't really follow this logic. I feel like the concept of a system ABI is fairly fundamental

To me the mixed ownership of the definitions of int, short, long etc between C, the cpu arch, and the OS, is nothing but a big legacy mess to avoid in the clean-slate systems I like to toy with in Rust.

I'm not convinced that the fact that they are isolated in another crate makes a successful static check any more likely -- the crate seems highly likely to appear in the dep-graph for any non-trivial system anyway.

Once can use [replace] in the workspace root to replace a disliked crate today with something that won't build. (I'd be tempted to do this for alloc with std-aware cargo for a hard real time system, too.) If it becomes idiomatic we could add a notion of a client-side yank/blacklist to make it more intuitive and not resolve blacklisted crates in build plans in the first place.

@jminer

The argument against adding C types to core to keep it pure doesn't seem very strong to me in this case

I agree with your comment, personally.

I'm not sure how to make progress on this RFC. In particular, there's a disconnect between the stated motivations, the mechanisms proposed, and the additional motivations being discussed on this thread. Partly because of this, the discussion is starting to go in circles.
I think we need to narrow our focus. To my mind, the most immediate pain point in the discussion that requires some kind of structural change to fix is the c_void problem (and as @jethrogb, the solution there would inform the other aspects of this RFC). So I'd propose we try to narrow the discussion down to just that point.
Would a proponent of defining c_void in libcore (or of defining it via an alias) be willing to write an RFC focused just on that problem? If we can reach consensus around such a design, I believe that would obviate the need for this RFC, and solve the more pressing problem at the same time.

I tried to code up something for this, but IMO there's no good way to define a c_void type in Rust, for the reasons I mentioned in this very long internals thread: https://internals.rust-lang.org/t/recent-change-to-make-exhaustiveness-and-uninhabited-types-play-nicer-together/4602.

See also #1861

In particular, if we go this route---https://github.com/rust-lang/rfcs/pull/1861#issuecomment-274931550, then std and libc can just alias that.

These types should be in libcore. They're not really  (or not only) C types, they are FFI/ABI types, which may also be used without libc.

The libs team discussed this RFC again during triage yesterday. The current consensus is to offer a canonical way of producing an "unknown, opaque type" (a better c_void), possible along the lines of #1861, or possibly just as a single type defined in libcore (with newtyping done separately). These options have been discussed up-thread already.
Once we make such a move, we'll be in a position to (1) redefine c_void everywhere in terms of such a type, making all instances interchangeable, (2) deprecate std::os::raw, which would then be purely type aliases, and (3) introduce a ctypes crate on crates.io which provides canonical (and compatible) type aliases.
As such, I'm going to propose to close this RFC for the time being, and encourage further discussion on #1861. We can reopen the avenue proposed by this RFC if the opaque type direction doesn't pan out.
@rfcbot fcp close

Can we move #1861 into FCP as well then? There hasn't been any activity there in about a month.

Looks like nothing new came up during FCP, so closing.

Once we make such a move, we'll be in a position to (1) redefine c_void everywhere in terms of such a type

I’m late to the party, but c_void cannot be made an extern type because that would change it from Sized (with size 1 byte) to !Sized, which would be a breaking change.

@SimonSapin we already have different incompatible definitions of c_void: libc::c_void and std::os::raw::c_void. The plan is to introduce a new canonical c_void for all users, deprecate std::os::raw and point libc::c_void at that new type.

New RFC to propose moving c_void (only, for now) to libcore: https://github.com/rust-lang/rfcs/pull/2521

For people still following this thread: #2521 is now in FCP and appears to take a very different direction than what was proposed here.

I know this is pretty old and was argued to death.
But there is a problem, and that's C FFI bindings that compiles to wasm32 with no-std. this means no-std and no libc right now.

But there is a problem, and that's C FFI bindings that compiles to wasm32 with no-std. this means no-std and no libc right now.

I agree. People are now asking me to add support to my crate for platforms that libc doesn't support, and for which libc refuses to add support. I think this RFC should be reopened since it solves exactly the problem that needs to be solved here for these embedded platforms and wasm32-unknown-unknown.

Still need as without ctype, it's create a gap between std and other no_std library.
As ctype is a de facto standard for FFI, creating lib_ctypes between core and std  is necessary.

Now that we have core::ffi we should just add the C types there.

Now that we have core::ffi we should just add the C types there.

Do I need create new PR for rfcs or just create a PR is enough?

A PR is enough.

Now that we have core::ffi we should just add the C types there.

I’m ok with that but I thought the reason this hadn’t happened a long time ago was desire to keep core OS-independent, but the definitions of C integer types are OS-dependent.
Also, assuming all of core::ffi is reexported at std::ffi, would we have both std::ffi::c_int and std::os::raw::c_int defining the same thing?

Now that we have core::ffi we should just add the C types there.

I’m ok with that but I thought the reason this hadn’t happened a long time ago was desire to keep core OS-independent, but the definitions of C integer types are OS-dependent.
Also, assuming all of core::ffi is reexported at std::ffi, would we have both std::ffi::c_int and std::os::raw::c_int defining the same thing?

yeap，its os dependes， but its only about ABI， not about runtime， there is no function linkage， so I think thats not an issue， of cuase， std.os.rae should re export core.ffi

Also, assuming all of core::ffi is reexported at std::ffi, would we have both std::ffi::c_int and std::os::raw::c_int defining the same thing?

We won't have std::ffi, std::os::raw enough.
People wan't ffi types can using core::ffi directly. std::ffi will be redundant.

source: gharchive/pull-request

authors: Amanieu Ericson2314 FenrirWolf SimonSapin alexcrichton archshift arielb1 aturon briansmith comex cuviper elichai jethrogb jmesmon jminer joshtriplett liigo lygstate nagisa nikomatsakis parched petrochenkov retep998 sfackler steveklabnik ubsan

repo: rust-lang/rfcs

license_type: permissive

license_source: github-api