Skip to content

Add the CTypes module for common C type aliases #14448

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions src/c_types.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# This module exposes the various common platform-dependent arithmetic types
# from the C language, to be used in lib bindings.
#
# For convenience, libs can import types from this module using aliases, so that
# the types can be used inside the lib without fully qualified paths:
#
# ```
# lib LibFoo
# alias Int = CTypes::Int
#
# fun foo(x : Int) : Int # okay
# fun bar(x : CTypes::Char*) # okay
# end
# ```
module CTypes
Copy link
Member

@straight-shoota straight-shoota Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: I was wondering if module C could be an option as it allows to conveniently write C::Char. But that would likely cause confusion because A, B, C are commonly used as stub constants.

Importing with a local alias (private alias C = ::CTypes) would be of similar convenience.

(ref #13504 (comment) ff.)

{% unless flag?(:bits32) || flag?(:bits64) %}
{% if target = Crystal.constant("TARGET_TRIPLE") %}
{% raise "Unsupported target: #{target.id}" %}
{% else %}
# this is for old compilers that attempt to directly use a newer stdlib
# for cross-compilation
{% raise "Architecture with unsupported word size" %}
{% end %}
{% end %}

# The C `char` type.
#
# Guaranteed to have at least 8 bits. Equivalent to `UInt8` on all currently
# supported targets.
alias Char = UInt8

# The C `signed char` type.
#
# Guaranteed to have at least 8 bits. Equivalent to `Int8` on all currently
# supported targets.
alias SChar = Int8

# The C `unsigned char` type.
#
# Guaranteed to have at least 8 bits. Equivalent to `UInt8` on all currently
# supported targets.
alias UChar = UInt8

# The C `short` type.
#
# Guaranteed to have at least 16 bits. Equivalent to `Int16` on all currently
# supported targets.
alias Short = Int16

# The C `unsigned short` type.
#
# Guaranteed to have at least 16 bits. Equivalent to `UInt16` on all currently
# supported targets.
alias UShort = UInt16

# The C `int` type.
#
# Guaranteed to have at least 16 bits. Equivalent to `Int32` on all currently
# supported targets.
alias Int = Int32

# The C `unsigned int` type.
#
# Guaranteed to have at least 16 bits. Equivalent to `UInt32` on all currently
# supported targets.
alias UInt = UInt32

# The C `long` type.
#
# Guaranteed to have at least 32 bits. Equivalent to `Int32` on Windows and
# on 32-bit targets. Equivalent to `Int64` on 64-bit, non-Windows targets.
{% if flag?(:bits32) || flag?(:win32) %}
alias Long = Int32
{% elsif flag?(:bits64) %}
alias Long = Int64
{% end %}

# The C `unsigned long` type.
#
# Guaranteed to have at least 32 bits. Equivalent to `UInt32` on Windows and
# on 32-bit targets. Equivalent to `UInt64` on 64-bit, non-Windows targets.
{% if flag?(:bits32) || flag?(:win32) %}
alias ULong = UInt32
{% elsif flag?(:bits64) %}
alias ULong = UInt64
{% end %}

# The C `long long` type.
#
# Guaranteed to have at least 64 bits. Equivalent to `Int64` on all currently
# supported targets.
alias LongLong = Int64

# The C `unsigned long long` type.
#
# Guaranteed to have at least 64 bits. Equivalent to `UInt64` on all currently
# supported targets.
alias ULongLong = UInt64

# The C `float` type.
#
# Equivalent to `Float32` on all currently supported targets.
alias Float = Float32

# The C `double` type.
#
# Equivalent to `Float64` on all currently supported targets.
alias Double = Float64

# The C `intptr_t` type.
#
# Large enough to hold the value of `Pointer#address`. Equivalent to `Int32`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Is this actually true? Isn't Pointer#address currently 64-bits on all architectures?

Copy link
Contributor Author

@HertzDevil HertzDevil Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more like "CTypes::UIntPtrT.new(ptr.address) should never raise OverflowError" on any target. And this should be true even if ptr = Pointer(Void).new(UInt64::MAX) on a 32-bit platform, as LibLLVM.build_int2ptr will zero-extend or truncate the address.

# on 32-bit targets, `Int64` on 64-bit targets.
{% if flag?(:bits32) %}
alias IntPtrT = Int32
{% elsif flag?(:bits64) %}
alias IntPtrT = Int64
{% end %}

# The C `uintptr_t` type.
#
# Large enough to hold the value of `Pointer#address`. Equivalent to `UInt32`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large enough to hold the value of Pointer#address.

This isn't true, uintptr_t is an integer type large enough to hold the pointer itself.

Namely, on 64-bit CHERI architectures, the pointer is 128 bits long (size_t or ptrdiff_t are 64 bits there).

There are other exotic architectures which also behave weirdly here, depending on the platform (IA-32 technically uses discontinuous 48-bit memory (16-bit segment-number plus 32-bit offset), but OSes normally move applications into a 32-bit segment to create the illusion of flat 32-bit memory).

# on 32-bit targets, `UInt64` on 64-bit targets.
{% if flag?(:bits32) %}
alias UIntPtrT = UInt32
{% elsif flag?(:bits64) %}
alias UIntPtrT = UInt64
{% end %}

# The C `size_t` type.
#
# Large enough to hold the value of `sizeof` for any type. Guaranteed to have
# at least 16 bits. Equivalent to `UInt32` on 32-bit targets, `UInt64` on
# 64-bit targets.
{% if flag?(:bits32) %}
alias SizeT = UInt32
{% elsif flag?(:bits64) %}
alias SizeT = UInt64
{% end %}

# The C `ptrdiff_t` type.
#
# Large enough to hold the value of `Pointer#-(Pointer)`. Guaranteed to have
# at least 17 bits. Equivalent to `Int32` on 32-bit targets, `Int64` on 64-bit
# targets.
{% if flag?(:bits32) %}
alias PtrDiffT = Int32
{% elsif flag?(:bits64) %}
alias PtrDiffT = Int64
{% end %}
end
37 changes: 15 additions & 22 deletions src/lib_c.cr
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
require "c_types"

{% if flag?(:win32) %}
@[Link({{ flag?(:static) ? "libucrt" : "ucrt" }})]
{% end %}
lib LibC
alias Char = UInt8
alias UChar = Char
alias SChar = Int8
alias Short = Int16
alias UShort = UInt16
alias Int = Int32
alias UInt = UInt32

{% if flag?(:bits32) || flag?(:win32) %}
alias Long = Int32
alias ULong = UInt32
{% elsif flag?(:bits64) %}
alias Long = Int64
alias ULong = UInt64
{% else %}
{% raise "Architecture with unsupported word size" %}
{% end %}

alias LongLong = Int64
alias ULongLong = UInt64
alias Float = Float32
alias Double = Float64
alias Char = CTypes::Char
alias UChar = CTypes::UChar
alias SChar = CTypes::SChar
alias Short = CTypes::Short
alias UShort = CTypes::UShort
alias Int = CTypes::Int
alias UInt = CTypes::UInt
alias Long = CTypes::Long
alias ULong = CTypes::ULong
alias LongLong = CTypes::LongLong
alias ULongLong = CTypes::ULongLong
alias Float = CTypes::Float
alias Double = CTypes::Double

{% if flag?(:android) %}
{% default_api_version = 31 %}
Expand Down
4 changes: 3 additions & 1 deletion src/lib_c/aarch64-android/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/aarch64-darwin/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/aarch64-linux-gnu/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/aarch64-linux-musl/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/arm-linux-gnueabihf/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = UInt
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/i386-linux-gnu/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = UInt
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/i386-linux-musl/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = UInt
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/wasm32-wasi/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-darwin/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-dragonfly/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-freebsd/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-linux-gnu/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-linux-musl/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-netbsd/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-openbsd/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-solaris/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = ULong
alias SizeT = CTypes::SizeT
end
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-windows-msvc/c/stddef.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require "c_types"

lib LibC
alias SizeT = UInt64
alias SizeT = CTypes::SizeT
end
6 changes: 4 additions & 2 deletions src/lib_c/x86_64-windows-msvc/c/stdint.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
require "c_types"

lib LibC
alias IntPtrT = Int64
alias UIntPtrT = UInt64
alias IntPtrT = CTypes::IntPtrT
alias UIntPtrT = CTypes::UIntPtrT
end
Loading