Merge pull request #1965 from ziglang/c-pointer-type

implement C pointers
This commit is contained in:
Andrew Kelley 2019-02-15 02:20:42 -05:00 committed by GitHub
commit 567c9b688e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1321 additions and 460 deletions

View File

@ -916,6 +916,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
std.zig.Token.Id.AngleBracketAngleBracketRightEqual,
std.zig.Token.Id.Tilde,
std.zig.Token.Id.BracketStarBracket,
std.zig.Token.Id.BracketStarCBracket,
=> try writeEscaped(out, src[token.start..token.end]),
std.zig.Token.Id.Invalid => return parseError(

View File

@ -1694,7 +1694,7 @@ test "comptime @intToPtr" {
}
}
{#code_end#}
{#see_also|Optional Pointers#}
{#see_also|Optional Pointers|@intToPtr|@ptrToInt#}
{#header_open|volatile#}
<p>Loads and stores are assumed to not have side effects. If a given load or store
should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.
@ -1823,7 +1823,9 @@ fn foo(bytes: []u8) u32 {
}
{#code_end#}
{#header_close#}
{#see_also|C Pointers#}
{#header_close#}
{#header_open|Slices#}
{#code_begin|test_safety|index out of bounds#}
const assert = @import("std").debug.assert;
@ -3981,7 +3983,7 @@ test "implicit cast - invoke a type as a function" {
{#code_end#}
<p>
Implicit casts are only allowed when it is completely unambiguous how to get from one type to another,
and the transformation is guaranteed to be safe.
and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.
</p>
{#header_open|Implicit Cast: Stricter Qualification#}
<p>
@ -6104,6 +6106,10 @@ test "call foo" {
<p>
Converts a pointer of one type to a pointer of another type.
</p>
<p>
{#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#}
to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}.
</p>
{#header_close#}
{#header_open|@ptrToInt#}
@ -7345,10 +7351,27 @@ fn bar(f: *Foo) void {
{#code_end#}
{#header_close#}
{#header_open|Out of Bounds Float To Integer Cast#}
{#header_open|Out of Bounds Float to Integer Cast#}
<p>TODO</p>
{#header_close#}
{#header_open|Pointer Cast Invalid Null#}
<p>At compile-time:</p>
{#code_begin|test_err|null pointer casted to type#}
comptime {
const opt_ptr: ?*i32 = null;
const ptr = @ptrCast(*i32, opt_ptr);
}
{#code_end#}
<p>At runtime:</p>
{#code_begin|exe_err#}
pub fn main() void {
var opt_ptr: ?*i32 = null;
var ptr = @ptrCast(*i32, opt_ptr);
}
{#code_end#}
{#header_close#}
{#header_close#}
{#header_open|Memory#}
<p>TODO: explain no default allocator in zig</p>
@ -7439,6 +7462,7 @@ pub fn main() void {
{#code_end#}
{#see_also|String Literals#}
{#header_close#}
{#header_open|Import from C Header File#}
<p>
The {#syntax#}@cImport{#endsyntax#} builtin function can be used
@ -7477,6 +7501,36 @@ const c = @cImport({
{#code_end#}
{#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}
{#header_close#}
{#header_open|C Pointers#}
<p>
This type is to be avoided whenever possible. The only valid reason for using a C pointer is in
auto-generated code from translating C code.
</p>
<p>
When importing C header files, it is ambiguous whether pointers should be translated as
single-item pointers ({#syntax#}*T{#endsyntax#}) or unknown-length pointers ({#syntax#}[*]T{#endsyntax#}).
C pointers are a compromise so that Zig code can utilize translated header files directly.
</p>
<p>{#syntax#}[*c]T{#endsyntax#} - C pointer.</p>
<ul>
<li>Supports all the syntax of the other two pointer types.</li>
<li>Implicitly casts to other pointer types, as well as {#link|Optional Pointers#}.
When a C pointer is implicitly casted to a non-optional pointer, safety-checked
{#link|Undefined Behavior#} occurs if the address is 0.
</li>
<li>Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked
{#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of
null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer
is unnecessary as one can use normal {#link|Optional Pointers#}.
</li>
<li>Supports {#link|implicit casting|Implicit Casts#} to and from integers.</li>
<li>Supports comparison with integers.</li>
<li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}
please!</li>
</ul>
{#header_close#}
{#header_open|Exporting a C Library#}
<p>
One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages
@ -8164,7 +8218,8 @@ ArrayTypeStart &lt;- LBRACKET Expr? RBRACKET
PtrTypeStart
&lt;- ASTERISK
/ ASTERISK2
/ LBRACKET ASTERISK RBRACKET
/ PTRUNKNOWN
/ PTRC
# ContainerDecl specific
ContainerDeclAuto &lt;- ContainerDeclType LBRACE ContainerMembers RBRACE
@ -8262,7 +8317,7 @@ LARROW2 &lt;- '&lt;&lt;' ![=] skip
LARROW2EQUAL &lt;- '&lt;&lt;=' skip
LARROWEQUAL &lt;- '&lt;=' skip
LBRACE &lt;- '{' skip
LBRACKET &lt;- '[' skip
LBRACKET &lt;- '[' ![*] skip
LPAREN &lt;- '(' skip
MINUS &lt;- '-' ![%=&gt;] skip
MINUSEQUAL &lt;- '-=' skip
@ -8279,6 +8334,8 @@ PLUS2 &lt;- '++' skip
PLUSEQUAL &lt;- '+=' skip
PLUSPERCENT &lt;- '+%' ![=] skip
PLUSPERCENTEQUAL &lt;- '+%=' skip
PTRC &lt;- '[*c]' skip
PTRUNKNOWN &lt;- '[*]' skip
QUESTIONMARK &lt;- '?' skip
RARROW &lt;- '&gt;' ![&gt;=] skip
RARROW2 &lt;- '&gt;&gt;' ![=] skip

View File

@ -137,10 +137,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
pub const ObjectFile = struct {
comp: *Compilation,
module: llvm.ModuleRef,
builder: llvm.BuilderRef,
module: *llvm.Module,
builder: *llvm.Builder,
dibuilder: *llvm.DIBuilder,
context: llvm.ContextRef,
context: *llvm.Context,
lock: event.Lock,
arena: *std.mem.Allocator,
@ -323,7 +323,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
fn addLLVMAttr(
ofile: *ObjectFile,
val: llvm.ValueRef,
val: *llvm.Value,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
) !void {
@ -335,7 +335,7 @@ fn addLLVMAttr(
fn addLLVMAttrStr(
ofile: *ObjectFile,
val: llvm.ValueRef,
val: *llvm.Value,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
attr_val: []const u8,
@ -351,7 +351,7 @@ fn addLLVMAttrStr(
}
fn addLLVMAttrInt(
val: llvm.ValueRef,
val: *llvm.Value,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
attr_val: u64,
@ -362,25 +362,25 @@ fn addLLVMAttrInt(
llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
}
fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void {
fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8) !void {
return addLLVMAttr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name);
}
fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void {
fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: []const u8) !void {
return addLLVMAttrStr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val);
}
fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: u64) !void {
return addLLVMAttrInt(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val);
}
fn renderLoadUntyped(
ofile: *ObjectFile,
ptr: llvm.ValueRef,
ptr: *llvm.Value,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
name: [*]const u8,
) !llvm.ValueRef {
) !*llvm.Value {
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
@ -390,11 +390,11 @@ fn renderLoadUntyped(
return result;
}
fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef {
fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value {
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
}
pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef {
pub fn getHandleValue(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer) !?*llvm.Value {
const child_type = ptr_type.key.child_type;
if (!child_type.hasBits()) {
return null;
@ -407,11 +407,11 @@ pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Po
pub fn renderStoreUntyped(
ofile: *ObjectFile,
value: llvm.ValueRef,
ptr: llvm.ValueRef,
value: *llvm.Value,
ptr: *llvm.Value,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
) !llvm.ValueRef {
) !*llvm.Value {
const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
@ -423,10 +423,10 @@ pub fn renderStoreUntyped(
pub fn renderStore(
ofile: *ObjectFile,
value: llvm.ValueRef,
ptr: llvm.ValueRef,
value: *llvm.Value,
ptr: *llvm.Value,
ptr_type: *Type.Pointer,
) !llvm.ValueRef {
) !*llvm.Value {
return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
}
@ -435,7 +435,7 @@ pub fn renderAlloca(
var_type: *Type,
name: []const u8,
alignment: Type.Pointer.Align,
) !llvm.ValueRef {
) !*llvm.Value {
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
@ -443,7 +443,7 @@ pub fn renderAlloca(
return result;
}
pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 {
pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: *llvm.Type) u32 {
return switch (alignment) {
Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
Type.Pointer.Align.Override => |a| a,

View File

@ -37,7 +37,7 @@ const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
/// Data that is local to the event loop.
pub const ZigCompiler = struct {
loop: *event.Loop,
llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
llvm_handle_pool: std.atomic.Stack(*llvm.Context),
lld_lock: event.Lock,
/// TODO pool these so that it doesn't have to lock
@ -60,7 +60,7 @@ pub const ZigCompiler = struct {
return ZigCompiler{
.loop = loop,
.lld_lock = event.Lock.init(loop),
.llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
.llvm_handle_pool = std.atomic.Stack(*llvm.Context).init(),
.prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
.native_libc = event.Future(LibCInstallation).init(loop),
};
@ -70,7 +70,7 @@ pub const ZigCompiler = struct {
fn deinit(self: *ZigCompiler) void {
self.lld_lock.deinit();
while (self.llvm_handle_pool.pop()) |node| {
c.LLVMContextDispose(node.data);
llvm.ContextDispose(node.data);
self.loop.allocator.destroy(node);
}
}
@ -80,11 +80,11 @@ pub const ZigCompiler = struct {
pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
errdefer c.LLVMContextDispose(context_ref);
const context_ref = llvm.ContextCreate() orelse return error.OutOfMemory;
errdefer llvm.ContextDispose(context_ref);
const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node);
node.* = std.atomic.Stack(llvm.ContextRef).Node{
const node = try self.loop.allocator.create(std.atomic.Stack(*llvm.Context).Node);
node.* = std.atomic.Stack(*llvm.Context).Node{
.next = undefined,
.data = context_ref,
};
@ -114,7 +114,7 @@ pub const ZigCompiler = struct {
};
pub const LlvmHandle = struct {
node: *std.atomic.Stack(llvm.ContextRef).Node,
node: *std.atomic.Stack(*llvm.Context).Node,
pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
zig_compiler.llvm_handle_pool.push(self.node);
@ -128,7 +128,7 @@ pub const Compilation = struct {
llvm_triple: Buffer,
root_src_path: ?[]const u8,
target: Target,
llvm_target: llvm.TargetRef,
llvm_target: *llvm.Target,
build_mode: builtin.Mode,
zig_lib_dir: []const u8,
zig_std_dir: []const u8,
@ -212,8 +212,8 @@ pub const Compilation = struct {
false_value: *Value.Bool,
noreturn_value: *Value.NoReturn,
target_machine: llvm.TargetMachineRef,
target_data_ref: llvm.TargetDataRef,
target_machine: *llvm.TargetMachine,
target_data_ref: *llvm.TargetData,
target_layout_str: [*]u8,
target_ptr_bits: u32,

View File

@ -67,7 +67,7 @@ pub const Inst = struct {
parent: ?*Inst,
/// populated durign codegen
llvm_value: ?llvm.ValueRef,
llvm_value: ?*llvm.Value,
pub fn cast(base: *Inst, comptime T: type) ?*T {
if (base.id == comptime typeToId(T)) {
@ -129,7 +129,7 @@ pub const Inst = struct {
}
}
pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {
pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?*llvm.Value) {
switch (base.id) {
Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
@ -313,10 +313,10 @@ pub const Inst = struct {
return new_inst;
}
pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
const fn_ref = self.params.fn_ref.llvm_value.?;
const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len);
const args = try ofile.arena.alloc(*llvm.Value, self.params.args.len);
for (self.params.args) |arg, i| {
args[i] = arg.llvm_value.?;
}
@ -360,7 +360,7 @@ pub const Inst = struct {
return new_inst;
}
pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
return self.base.val.KnownValue.getLlvmConst(ofile);
}
};
@ -392,7 +392,7 @@ pub const Inst = struct {
return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
}
pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
const value = self.params.return_value.llvm_value;
const return_type = self.params.return_value.getKnownType();
@ -540,7 +540,7 @@ pub const Inst = struct {
}
}
pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef {
pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) *llvm.Value {
switch (self.params.var_scope.data) {
Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass
Scope.Var.Data.Param => |param| return param.llvm_value,
@ -596,7 +596,7 @@ pub const Inst = struct {
return new_inst;
}
pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
const child_type = self.base.getKnownType();
if (!child_type.hasBits()) {
return null;
@ -935,8 +935,8 @@ pub const BasicBlock = struct {
ref_instruction: ?*Inst,
/// for codegen
llvm_block: llvm.BasicBlockRef,
llvm_exit_block: llvm.BasicBlockRef,
llvm_block: *llvm.BasicBlock,
llvm_exit_block: *llvm.BasicBlock,
/// the basic block that is derived from this one in analysis
child: ?*BasicBlock,

View File

@ -154,8 +154,8 @@ pub const LibCInstallation = struct {
c.ZigFindWindowsSdkError.None => {
windows_sdk = sdk;
if (sdk.msvc_lib_dir_ptr) |ptr| {
self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
if (sdk.msvc_lib_dir_ptr != 0) {
self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
}
try group.call(findNativeKernel32LibDir, self, loop, sdk);
try group.call(findNativeIncludeDirWindows, self, loop, sdk);
@ -437,20 +437,20 @@ const Search = struct {
fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
var search_end: usize = 0;
if (sdk.path10_ptr) |path10_ptr| {
if (sdk.version10_ptr) |ver10_ptr| {
if (sdk.path10_ptr != 0) {
if (sdk.version10_ptr != 0) {
search_buf[search_end] = Search{
.path = path10_ptr[0..sdk.path10_len],
.version = ver10_ptr[0..sdk.version10_len],
.path = sdk.path10_ptr[0..sdk.path10_len],
.version = sdk.version10_ptr[0..sdk.version10_len],
};
search_end += 1;
}
}
if (sdk.path81_ptr) |path81_ptr| {
if (sdk.version81_ptr) |ver81_ptr| {
if (sdk.path81_ptr != 0) {
if (sdk.version81_ptr != 0) {
search_buf[search_end] = Search{
.path = path81_ptr[0..sdk.path81_len],
.version = ver81_ptr[0..sdk.version81_len],
.path = sdk.path81_ptr[0..sdk.path81_len],
.version = sdk.version81_ptr[0..sdk.version81_len],
};
search_end += 1;
}

View File

@ -11,45 +11,31 @@ const assert = @import("std").debug.assert;
pub const AttributeIndex = c_uint;
pub const Bool = c_int;
pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
pub const ContextRef = removeNullability(c.LLVMContextRef);
pub const ModuleRef = removeNullability(c.LLVMModuleRef);
pub const ValueRef = removeNullability(c.LLVMValueRef);
pub const TypeRef = removeNullability(c.LLVMTypeRef);
pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
pub const TargetRef = removeNullability(c.LLVMTargetRef);
pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
pub const Builder = c.LLVMBuilderRef.Child.Child;
pub const Context = c.LLVMContextRef.Child.Child;
pub const Module = c.LLVMModuleRef.Child.Child;
pub const Value = c.LLVMValueRef.Child.Child;
pub const Type = c.LLVMTypeRef.Child.Child;
pub const BasicBlock = c.LLVMBasicBlockRef.Child.Child;
pub const Attribute = c.LLVMAttributeRef.Child.Child;
pub const Target = c.LLVMTargetRef.Child.Child;
pub const TargetMachine = c.LLVMTargetMachineRef.Child.Child;
pub const TargetData = c.LLVMTargetDataRef.Child.Child;
pub const DIBuilder = c.ZigLLVMDIBuilder;
pub const DIFile = c.ZigLLVMDIFile;
pub const DICompileUnit = c.ZigLLVMDICompileUnit;
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
pub const AddFunction = c.LLVMAddFunction;
pub const AddGlobal = c.LLVMAddGlobal;
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
pub const ArrayType = c.LLVMArrayType;
pub const BuildLoad = c.LLVMBuildLoad;
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstArray = c.LLVMConstArray;
pub const ConstBitCast = c.LLVMConstBitCast;
pub const ConstInt = c.LLVMConstInt;
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
pub const ConstNeg = c.LLVMConstNeg;
pub const ConstNull = c.LLVMConstNull;
pub const ConstStringInContext = c.LLVMConstStringInContext;
pub const ConstStructInContext = c.LLVMConstStructInContext;
pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
pub const CreateFile = c.ZigLLVMCreateFile;
pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
pub const DisposeBuilder = c.LLVMDisposeBuilder;
pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
@ -62,9 +48,7 @@ pub const DumpModule = c.LLVMDumpModule;
pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
pub const GetUndef = c.LLVMGetUndef;
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
@ -81,14 +65,11 @@ pub const Int64TypeInContext = c.LLVMInt64TypeInContext;
pub const Int8TypeInContext = c.LLVMInt8TypeInContext;
pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext;
pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext;
pub const IntTypeInContext = c.LLVMIntTypeInContext;
pub const LabelTypeInContext = c.LLVMLabelTypeInContext;
pub const MDNodeInContext = c.LLVMMDNodeInContext;
pub const MDStringInContext = c.LLVMMDStringInContext;
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
pub const PointerType = c.LLVMPointerType;
pub const SetAlignment = c.LLVMSetAlignment;
pub const SetDataLayout = c.LLVMSetDataLayout;
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
@ -99,50 +80,146 @@ pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
pub const SetVolatile = c.LLVMSetVolatile;
pub const StructTypeInContext = c.LLVMStructTypeInContext;
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
pub const AddGlobal = LLVMAddGlobal;
extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*]const u8) ?*Value;
pub const ConstStringInContext = LLVMConstStringInContext;
extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
pub const ConstInt = LLVMConstInt;
extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value;
pub const BuildLoad = LLVMBuildLoad;
extern fn LLVMBuildLoad(arg0: *Builder, PointerVal: *Value, Name: [*]const u8) ?*Value;
pub const ConstNull = LLVMConstNull;
extern fn LLVMConstNull(Ty: *Type) ?*Value;
pub const CreateStringAttribute = LLVMCreateStringAttribute;
extern fn LLVMCreateStringAttribute(
C: *Context,
K: [*]const u8,
KLength: c_uint,
V: [*]const u8,
VLength: c_uint,
) ?*Attribute;
pub const CreateEnumAttribute = LLVMCreateEnumAttribute;
extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) ?*Attribute;
pub const AddFunction = LLVMAddFunction;
extern fn LLVMAddFunction(M: *Module, Name: [*]const u8, FunctionTy: *Type) ?*Value;
pub const CreateCompileUnit = ZigLLVMCreateCompileUnit;
extern fn ZigLLVMCreateCompileUnit(
dibuilder: *DIBuilder,
lang: c_uint,
difile: *DIFile,
producer: [*]const u8,
is_optimized: bool,
flags: [*]const u8,
runtime_version: c_uint,
split_name: [*]const u8,
dwo_id: u64,
emit_debug_info: bool,
) ?*DICompileUnit;
pub const CreateFile = ZigLLVMCreateFile;
extern fn ZigLLVMCreateFile(dibuilder: *DIBuilder, filename: [*]const u8, directory: [*]const u8) ?*DIFile;
pub const ArrayType = LLVMArrayType;
extern fn LLVMArrayType(ElementType: *Type, ElementCount: c_uint) ?*Type;
pub const CreateDIBuilder = ZigLLVMCreateDIBuilder;
extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) ?*DIBuilder;
pub const PointerType = LLVMPointerType;
extern fn LLVMPointerType(ElementType: *Type, AddressSpace: c_uint) ?*Type;
pub const CreateBuilderInContext = LLVMCreateBuilderInContext;
extern fn LLVMCreateBuilderInContext(C: *Context) ?*Builder;
pub const IntTypeInContext = LLVMIntTypeInContext;
extern fn LLVMIntTypeInContext(C: *Context, NumBits: c_uint) ?*Type;
pub const ModuleCreateWithNameInContext = LLVMModuleCreateWithNameInContext;
extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*]const u8, C: *Context) ?*Module;
pub const VoidTypeInContext = LLVMVoidTypeInContext;
extern fn LLVMVoidTypeInContext(C: *Context) ?*Type;
pub const ContextCreate = LLVMContextCreate;
extern fn LLVMContextCreate() ?*Context;
pub const ContextDispose = LLVMContextDispose;
extern fn LLVMContextDispose(C: *Context) void;
pub const CopyStringRepOfTargetData = LLVMCopyStringRepOfTargetData;
extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*]u8;
pub const CreateTargetDataLayout = LLVMCreateTargetDataLayout;
extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData;
pub const CreateTargetMachine = LLVMCreateTargetMachine;
extern fn LLVMCreateTargetMachine(
T: *Target,
Triple: [*]const u8,
CPU: [*]const u8,
Features: [*]const u8,
Level: CodeGenOptLevel,
Reloc: RelocMode,
CodeModel: CodeModel,
) ?*TargetMachine;
pub const GetHostCPUName = LLVMGetHostCPUName;
extern fn LLVMGetHostCPUName() ?[*]u8;
pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
extern fn ZigLLVMGetNativeFeatures() ?[*]u8;
pub const GetElementType = LLVMGetElementType;
extern fn LLVMGetElementType(Ty: TypeRef) TypeRef;
extern fn LLVMGetElementType(Ty: *Type) *Type;
pub const TypeOf = LLVMTypeOf;
extern fn LLVMTypeOf(Val: ValueRef) TypeRef;
extern fn LLVMTypeOf(Val: *Value) *Type;
pub const BuildStore = LLVMBuildStore;
extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef;
extern fn LLVMBuildStore(arg0: *Builder, Val: *Value, Ptr: *Value) ?*Value;
pub const BuildAlloca = LLVMBuildAlloca;
extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef;
extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*]const u8) ?*Value;
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
pub extern fn LLVMConstInBoundsGEP(ConstantVal: *Value, ConstantIndices: [*]*Value, NumIndices: c_uint) ?*Value;
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: **Target, ErrorMessage: ?*[*]u8) Bool;
pub const VerifyModule = LLVMVerifyModule;
extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
pub const GetInsertBlock = LLVMGetInsertBlock;
extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef;
extern fn LLVMGetInsertBlock(Builder: *Builder) *BasicBlock;
pub const FunctionType = LLVMFunctionType;
extern fn LLVMFunctionType(
ReturnType: TypeRef,
ParamTypes: [*]TypeRef,
ReturnType: *Type,
ParamTypes: [*]*Type,
ParamCount: c_uint,
IsVarArg: Bool,
) ?TypeRef;
) ?*Type;
pub const GetParam = LLVMGetParam;
extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef;
extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value;
pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext;
extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef;
extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*]const u8) ?*BasicBlock;
pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd;
extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void;
extern fn LLVMPositionBuilderAtEnd(Builder: *Builder, Block: *BasicBlock) void;
pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction;
pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
@ -190,17 +267,17 @@ pub const FnInline = extern enum {
};
fn removeNullability(comptime T: type) type {
comptime assert(@typeId(T) == builtin.TypeId.Optional);
return T.Child;
comptime assert(@typeInfo(T).Pointer.size == @import("builtin").TypeInfo.Pointer.Size.C);
return *T.Child;
}
pub const BuildRet = LLVMBuildRet;
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
extern fn LLVMBuildRet(arg0: *Builder, V: ?*Value) ?*Value;
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
extern fn ZigLLVMTargetMachineEmitToFile(
targ_machine_ref: TargetMachineRef,
module_ref: ModuleRef,
targ_machine_ref: *TargetMachine,
module_ref: *Module,
filename: [*]const u8,
output_type: EmitOutputType,
error_message: *[*]u8,
@ -209,6 +286,6 @@ extern fn ZigLLVMTargetMachineEmitToFile(
) bool;
pub const BuildCall = ZigLLVMBuildCall;
extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?*Value;
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;

View File

@ -362,7 +362,7 @@ pub const Scope = struct {
pub const Param = struct {
index: usize,
typ: *Type,
llvm_value: llvm.ValueRef,
llvm_value: *llvm.Value,
};
pub fn createParam(

View File

@ -457,8 +457,8 @@ pub const Target = union(enum) {
}
}
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
var result: llvm.TargetRef = undefined;
pub fn llvmTargetFromTriple(triple: std.Buffer) !*llvm.Target {
var result: *llvm.Target = undefined;
var err_msg: [*]u8 = undefined;
if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);

View File

@ -51,8 +51,8 @@ pub const Type = struct {
pub fn getLlvmType(
base: *Type,
allocator: *Allocator,
llvm_context: llvm.ContextRef,
) (error{OutOfMemory}!llvm.TypeRef) {
llvm_context: *llvm.Context,
) (error{OutOfMemory}!*llvm.Type) {
switch (base.id) {
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
@ -196,7 +196,7 @@ pub const Type = struct {
}
/// If you have an llvm conext handy, you can use it here.
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
@ -205,7 +205,7 @@ pub const Type = struct {
}
/// Lower level function that does the work. See getAbiAlignment.
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
}
@ -218,7 +218,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -496,13 +496,13 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
const normal = &self.key.data.Normal;
const llvm_return_type = switch (normal.return_type.id) {
Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
else => try normal.return_type.getLlvmType(allocator, llvm_context),
};
const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len);
const llvm_param_types = try allocator.alloc(*llvm.Type, normal.params.len);
defer allocator.free(llvm_param_types);
for (llvm_param_types) |*llvm_param_type, i| {
llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
@ -559,7 +559,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -658,7 +658,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
}
};
@ -670,7 +670,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -794,6 +794,7 @@ pub const Type = struct {
Size.One => "*",
Size.Many => "[*]",
Size.Slice => "[]",
Size.C => "[*c]",
};
const mut_str = switch (self.key.mut) {
Mut.Const => "const ",
@ -835,7 +836,7 @@ pub const Type = struct {
return self;
}
pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
}
@ -903,7 +904,7 @@ pub const Type = struct {
return self;
}
pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
}
@ -916,7 +917,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -966,7 +967,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -978,7 +979,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -990,7 +991,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -1002,7 +1003,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -1014,7 +1015,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -1034,7 +1035,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -1054,7 +1055,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -1066,7 +1067,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@ -1088,6 +1089,7 @@ fn hashAny(x: var, comptime seed: u64) u32 {
builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
builtin.TypeInfo.Pointer.Size.C => unreachable,
}
},
builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),

View File

@ -57,7 +57,7 @@ pub const Value = struct {
std.debug.warn("{}", @tagName(base.id));
}
pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?*llvm.Value) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
@ -153,7 +153,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?*llvm.Value {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
@ -238,7 +238,7 @@ pub const Value = struct {
/// We know that the function definition will end up in an .o file somewhere.
/// Here, all we have to do is generate a global prototype.
/// TODO cache the prototype per ObjectFile
pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef {
pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?*llvm.Value {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
@ -283,8 +283,8 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
const llvm_type = llvm.Int1TypeInContext(ofile.context);
pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) !?*llvm.Value {
const llvm_type = llvm.Int1TypeInContext(ofile.context) orelse return error.OutOfMemory;
if (self.x) {
return llvm.ConstAllOnes(llvm_type);
} else {
@ -381,7 +381,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?*llvm.Value {
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
switch (self.special) {
@ -391,7 +391,7 @@ pub const Value = struct {
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
const indices = []llvm.ValueRef{
const indices = []*llvm.Value{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
@ -459,7 +459,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value {
switch (self.special) {
Special.Undefined => {
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
@ -534,7 +534,7 @@ pub const Value = struct {
return self;
}
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?*llvm.Value {
switch (self.base.typ.id) {
Type.Id.Int => {
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);

View File

@ -691,15 +691,17 @@ struct AstNodePointerType {
AstNode *align_expr;
BigInt *bit_offset_start;
BigInt *host_int_bytes;
AstNode *op_expr;
Token *allow_zero_token;
bool is_const;
bool is_volatile;
AstNode *op_expr;
};
struct AstNodeArrayType {
AstNode *size;
AstNode *child_type;
AstNode *align_expr;
Token *allow_zero_token;
bool is_const;
bool is_volatile;
};
@ -1038,6 +1040,7 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
enum PtrLen {
PtrLenUnknown,
PtrLenSingle,
PtrLenC,
};
struct ZigTypePointer {
@ -1049,6 +1052,7 @@ struct ZigTypePointer {
uint32_t host_int_bytes; // size of host integer. 0 means no host integer; this field is aligned
bool is_const;
bool is_volatile;
bool allow_zero;
};
struct ZigTypeInt {
@ -1484,6 +1488,7 @@ enum PanicMsgId {
PanicMsgIdBadUnionField,
PanicMsgIdBadEnumValue,
PanicMsgIdFloatToInt,
PanicMsgIdPtrCastNull,
PanicMsgIdCount,
};
@ -1498,11 +1503,12 @@ struct TypeId {
struct {
ZigType *child_type;
PtrLen ptr_len;
bool is_const;
bool is_volatile;
uint32_t alignment;
uint32_t bit_offset_in_host;
uint32_t host_int_bytes;
bool is_const;
bool is_volatile;
bool allow_zero;
} pointer;
struct {
ZigType *child_type;
@ -2591,6 +2597,7 @@ struct IrInstructionPtrType {
PtrLen ptr_len;
bool is_const;
bool is_volatile;
bool allow_zero;
};
struct IrInstructionPromiseType {
@ -2606,6 +2613,7 @@ struct IrInstructionSliceType {
IrInstruction *child_type;
bool is_const;
bool is_volatile;
bool allow_zero;
};
struct IrInstructionAsm {
@ -2994,12 +3002,14 @@ struct IrInstructionPtrCastSrc {
IrInstruction *dest_type;
IrInstruction *ptr;
bool safety_check_on;
};
struct IrInstructionPtrCastGen {
IrInstruction base;
IrInstruction *ptr;
bool safety_check_on;
};
struct IrInstructionBitCast {

View File

@ -417,10 +417,25 @@ ZigType *get_promise_type(CodeGen *g, ZigType *result_type) {
return entry;
}
static const char *ptr_len_to_star_str(PtrLen ptr_len) {
switch (ptr_len) {
case PtrLenSingle:
return "*";
case PtrLenUnknown:
return "[*]";
case PtrLenC:
return "[*c]";
}
zig_unreachable();
}
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
{
// TODO when implementing https://github.com/ziglang/zig/issues/1953
// move this to a parameter
bool allow_zero = (ptr_len == PtrLenC);
assert(!type_is_invalid(child_type));
assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
@ -440,7 +455,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
TypeId type_id = {};
ZigType **parent_pointer = nullptr;
if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle) {
if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) {
type_id.id = ZigTypeIdPointer;
type_id.data.pointer.child_type = child_type;
type_id.data.pointer.is_const = is_const;
@ -449,6 +464,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
type_id.data.pointer.bit_offset_in_host = bit_offset_in_host;
type_id.data.pointer.host_int_bytes = host_int_bytes;
type_id.data.pointer.ptr_len = ptr_len;
type_id.data.pointer.allow_zero = allow_zero;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry)
@ -466,21 +482,31 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]";
const char *star_str = ptr_len_to_star_str(ptr_len);
const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
const char *allow_zero_str;
if (ptr_len == PtrLenC) {
assert(allow_zero);
allow_zero_str = "";
} else {
allow_zero_str = allow_zero ? "allowzero " : "";
}
buf_resize(&entry->name, 0);
if (host_int_bytes == 0 && byte_alignment == 0) {
buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name));
buf_appendf(&entry->name, "%s%s%s%s%s",
star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (host_int_bytes == 0) {
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment,
const_str, volatile_str, buf_ptr(&child_type->name));
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (byte_alignment == 0) {
buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str,
bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name));
buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str,
bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
buf_ptr(&child_type->name));
} else {
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment,
bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name));
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
buf_ptr(&child_type->name));
}
assert(child_type->id != ZigTypeIdInvalid);
@ -488,7 +514,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
entry->zero_bits = !type_has_bits(child_type);
if (!entry->zero_bits) {
if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || bit_offset_in_host != 0) {
if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
bit_offset_in_host != 0 || allow_zero)
{
ZigType *peer_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenSingle, 0, 0, host_int_bytes);
entry->type_ref = peer_type->type_ref;
@ -522,6 +550,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
entry->data.pointer.explicit_alignment = byte_alignment;
entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
entry->data.pointer.host_int_bytes = host_int_bytes;
entry->data.pointer.allow_zero = allow_zero;
if (parent_pointer) {
*parent_pointer = entry;
@ -838,7 +867,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
ZigType *child_type = ptr_type->data.pointer.child_type;
if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
ptr_type->data.pointer.explicit_alignment != 0)
ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
{
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenUnknown, 0, 0, 0);
@ -861,7 +890,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(child_ptr_type->id == ZigTypeIdPointer);
if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
child_ptr_type->data.pointer.explicit_alignment != 0)
child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero)
{
ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
@ -1457,7 +1486,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
zig_unreachable();
}
static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
@ -2650,6 +2679,13 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
buf_sprintf("enums, not structs, support field assignment"));
}
if (field_type->id == ZigTypeIdOpaque) {
add_node_error(g, field_node->data.struct_field.type,
buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs"));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
continue;
}
switch (type_requires_comptime(g, field_type)) {
case ReqCompTimeYes:
struct_type->data.structure.requires_comptime = true;
@ -2934,6 +2970,13 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
}
union_field->type_entry = field_type;
if (field_type->id == ZigTypeIdOpaque) {
add_node_error(g, field_node->data.struct_field.type,
buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in unions"));
union_type->data.unionation.is_invalid = true;
continue;
}
switch (type_requires_comptime(g, field_type)) {
case ReqCompTimeInvalid:
union_type->data.unionation.is_invalid = true;
@ -4041,7 +4084,9 @@ ZigType *get_src_ptr_type(ZigType *type) {
if (type->id == ZigTypeIdFn) return type;
if (type->id == ZigTypeIdPromise) return type;
if (type->id == ZigTypeIdOptional) {
if (type->data.maybe.child_type->id == ZigTypeIdPointer) return type->data.maybe.child_type;
if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
}
if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
if (type->data.maybe.child_type->id == ZigTypeIdPromise) return type->data.maybe.child_type;
}
@ -4055,6 +4100,10 @@ ZigType *get_codegen_ptr_type(ZigType *type) {
return ty;
}
bool type_is_nonnull_ptr(ZigType *type) {
return type_is_codegen_pointer(type) && !ptr_allows_addr_zero(type);
}
bool type_is_codegen_pointer(ZigType *type) {
return get_codegen_ptr_type(type) == type;
}
@ -6300,6 +6349,7 @@ uint32_t type_id_hash(TypeId x) {
((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
(x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
(((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
(((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
(((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881);
@ -6350,6 +6400,7 @@ bool type_id_eql(TypeId a, TypeId b) {
a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
a.data.pointer.is_const == b.data.pointer.is_const &&
a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
a.data.pointer.alignment == b.data.pointer.alignment &&
a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes;
@ -6883,3 +6934,21 @@ Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_no
return ErrorNone;
}
const char *container_string(ContainerKind kind) {
switch (kind) {
case ContainerKindEnum: return "enum";
case ContainerKindStruct: return "struct";
case ContainerKindUnion: return "union";
}
zig_unreachable();
}
bool ptr_allows_addr_zero(ZigType *ptr_type) {
if (ptr_type->id == ZigTypeIdPointer) {
return ptr_type->data.pointer.allow_zero;
} else if (ptr_type->id == ZigTypeIdOptional) {
return true;
}
return false;
}

View File

@ -44,7 +44,9 @@ void find_libc_include_path(CodeGen *g);
void find_libc_lib_path(CodeGen *g);
bool type_has_bits(ZigType *type_entry);
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
bool ptr_allows_addr_zero(ZigType *ptr_type);
bool type_is_nonnull_ptr(ZigType *type);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
@ -215,6 +217,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty);
bool type_is_c_abi_int(CodeGen *g, ZigType *ty);
bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id);
const char *container_string(ContainerKind kind);
uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field);

View File

@ -136,13 +136,19 @@ static const char *thread_local_string(Token *tok) {
return (tok == nullptr) ? "" : "threadlocal ";
}
const char *container_string(ContainerKind kind) {
switch (kind) {
case ContainerKindEnum: return "enum";
case ContainerKindStruct: return "struct";
case ContainerKindUnion: return "union";
static const char *token_to_ptr_len_str(Token *tok) {
assert(tok != nullptr);
switch (tok->id) {
case TokenIdStar:
case TokenIdStarStar:
return "*";
case TokenIdBracketStarBracket:
return "[*]";
case TokenIdBracketStarCBracket:
return "[*c]";
default:
zig_unreachable();
}
zig_unreachable();
}
static const char *node_type_str(NodeType node_type) {
@ -644,13 +650,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypePointerType:
{
if (!grouped) fprintf(ar->f, "(");
const char *star = "[*]";
if (node->data.pointer_type.star_token != nullptr &&
(node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar))
{
star = "*";
}
fprintf(ar->f, "%s", star);
const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token);
fprintf(ar->f, "%s", ptr_len_str);
if (node->data.pointer_type.align_expr != nullptr) {
fprintf(ar->f, "align(");
render_node_grouped(ar, node->data.pointer_type.align_expr);

View File

@ -17,7 +17,4 @@ void ast_print(FILE *f, AstNode *node, int indent);
void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size);
const char *container_string(ContainerKind kind);
#endif

View File

@ -617,9 +617,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
unsigned init_gen_i = 0;
if (!type_has_bits(return_type)) {
// nothing to do
} else if (type_is_codegen_pointer(return_type)) {
} else if (type_is_nonnull_ptr(return_type)) {
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
} else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
// Sret pointers must not be address 0
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
if (cc_want_sret_attr(cc)) {
@ -637,6 +638,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
// Error return trace memory is in the stack, which is impossible to be at address 0
// on any architecture.
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
@ -950,6 +953,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("invalid enum value");
case PanicMsgIdFloatToInt:
return buf_create_from_str("integer part of floating point value out of bounds");
case PanicMsgIdPtrCastNull:
return buf_create_from_str("cast causes pointer to be null");
}
zig_unreachable();
}
@ -1244,6 +1249,8 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
// Error return trace memory is in the stack, which is impossible to be at address 0
// on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@ -1318,9 +1325,13 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
// Error return trace memory is in the stack, which is impossible to be at address 0
// on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)0, "noalias");
addLLVMArgAttr(fn_val, (unsigned)0, "writeonly");
// Error return trace memory is in the stack, which is impossible to be at address 0
// on any architecture.
addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)1, "noalias");
addLLVMArgAttr(fn_val, (unsigned)1, "readonly");
@ -1448,6 +1459,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
// Error return trace memory is in the stack, which is impossible to be at address 0
// on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@ -2049,7 +2062,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
case FnWalkIdAttrs: {
ZigType *ptr_type = get_codegen_ptr_type(ty);
if (ptr_type != nullptr) {
if (ty->id != ZigTypeIdOptional) {
if (type_is_nonnull_ptr(ty)) {
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
}
if (ptr_type->data.pointer.is_const) {
@ -2093,6 +2106,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
assert(handle_is_ptr(ty));
switch (fn_walk->id) {
case FnWalkIdAttrs:
// arrays passed to C ABI functions may not be at address 0
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
fn_walk->data.attrs.gen_i += 1;
@ -2132,6 +2146,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
case FnWalkIdAttrs:
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval");
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
// Byvalue parameters must not have address 0
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
fn_walk->data.attrs.gen_i += 1;
break;
@ -2264,7 +2279,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) {
if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) {
addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly");
}
if (param_type->id == ZigTypeIdPointer) {
if (type_is_nonnull_ptr(param_type)) {
addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull");
}
break;
@ -2657,7 +2672,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
(op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) ||
(op1->value.type->id == ZigTypeIdPointer &&
(op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
op1->value.type->data.pointer.ptr_len != PtrLenSingle)
);
ZigType *operand_type = op1->value.type;
ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
@ -2716,7 +2731,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
AddSubMulMul;
if (scalar_type->id == ZigTypeIdPointer) {
assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown);
assert(scalar_type->data.pointer.ptr_len != PtrLenSingle);
LLVMValueRef subscript_value;
if (operand_type->id == ZigTypeIdVector)
zig_panic("TODO: Implement vector operations on pointers.");
@ -3028,7 +3043,22 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
return nullptr;
}
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
if (!want_safety_check || ptr_allows_addr_zero(wanted_type))
return result_ptr;
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr));
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, "");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk");
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block);
gen_safety_crash(g, PanicMsgIdPtrCastNull);
LLVMPositionBuilderAtEnd(g->builder, ok_block);
return result_ptr;
}
static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable,
@ -7294,6 +7324,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" One,\n"
" Many,\n"
" Slice,\n"
" C,\n"
" };\n"
" };\n"
"\n"

File diff suppressed because it is too large Load Diff

View File

@ -2778,7 +2778,8 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) {
// PtrTypeStart
// <- ASTERISK
// / ASTERISK2
// / LBRACKET ASTERISK RBRACKET
// / PTRUNKNOWN
// / PTRC
static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
Token *asterisk = eat_token_if(pc, TokenIdStar);
if (asterisk != nullptr) {
@ -2804,6 +2805,13 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
return res;
}
Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket);
if (cptr != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr);
res->data.pointer_type.star_token = cptr;
return res;
}
return nullptr;
}

View File

@ -807,6 +807,10 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
zig_unreachable();
}
bool target_allows_addr_zero(const ZigTarget *target) {
return target->os == OsFreestanding;
}
const char *target_o_file_ext(ZigTarget *target) {
if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) {
return ".obj";

View File

@ -135,5 +135,6 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target
ZigLLVM_OSType get_llvm_os_type(Os os_type);
bool target_is_arm(const ZigTarget *target);
bool target_allows_addr_zero(const ZigTarget *target);
#endif

View File

@ -221,6 +221,7 @@ enum TokenizeState {
TokenizeStateError,
TokenizeStateLBracket,
TokenizeStateLBracketStar,
TokenizeStateLBracketStarC,
};
@ -846,7 +847,6 @@ void tokenize(Buf *buf, Tokenization *out) {
switch (c) {
case '*':
t.state = TokenizeStateLBracketStar;
set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
break;
default:
// reinterpret as just an lbracket
@ -857,6 +857,21 @@ void tokenize(Buf *buf, Tokenization *out) {
}
break;
case TokenizeStateLBracketStar:
switch (c) {
case 'c':
t.state = TokenizeStateLBracketStarC;
set_token_id(&t, t.cur_tok, TokenIdBracketStarCBracket);
break;
case ']':
set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
end_token(&t);
t.state = TokenizeStateStart;
break;
default:
invalid_char_error(&t, c);
}
break;
case TokenizeStateLBracketStarC:
switch (c) {
case ']':
end_token(&t);
@ -1491,6 +1506,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateLineStringContinue:
case TokenizeStateLineStringContinueC:
case TokenizeStateLBracketStar:
case TokenizeStateLBracketStarC:
tokenize_error(&t, "unexpected EOF");
break;
case TokenizeStateLineComment:
@ -1528,6 +1544,7 @@ const char * token_name(TokenId id) {
case TokenIdBitShiftRightEq: return ">>=";
case TokenIdBitXorEq: return "^=";
case TokenIdBracketStarBracket: return "[*]";
case TokenIdBracketStarCBracket: return "[*c]";
case TokenIdCharLiteral: return "CharLiteral";
case TokenIdCmpEq: return "==";
case TokenIdCmpGreaterOrEq: return ">=";

View File

@ -29,6 +29,7 @@ enum TokenId {
TokenIdBitShiftRightEq,
TokenIdBitXorEq,
TokenIdBracketStarBracket,
TokenIdBracketStarCBracket,
TokenIdCharLiteral,
TokenIdCmpEq,
TokenIdCmpGreaterOrEq,

View File

@ -291,11 +291,22 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod
node);
}
static TokenId ptr_len_to_token_id(PtrLen ptr_len) {
switch (ptr_len) {
case PtrLenSingle:
return TokenIdStar;
case PtrLenUnknown:
return TokenIdBracketStarBracket;
case PtrLenC:
return TokenIdBracketStarCBracket;
}
zig_unreachable();
}
static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) {
AstNode *node = trans_create_node(c, NodeTypePointerType);
node->data.pointer_type.star_token = allocate<ZigToken>(1);
node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket;
node->data.pointer_type.is_const = is_const;
node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len);
node->data.pointer_type.is_const = is_const;
node->data.pointer_type.is_volatile = is_volatile;
node->data.pointer_type.op_expr = child_node;
@ -925,11 +936,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
}
PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown;
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_node, ptr_len);
return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
if (type_is_opaque(c, child_qt.getTypePtr(), source_loc)) {
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_node, PtrLenSingle);
return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
} else {
return trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_node, PtrLenC);
}
}
case Type::Typedef:
{
@ -1113,7 +1127,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
return nullptr;
}
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown);
child_qt.isVolatileQualified(), child_type_node, PtrLenC);
return pointer_node;
}
case Type::BlockPointer:
@ -1693,7 +1707,7 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im
return node;
}
case CK_NullToPointer:
return trans_create_node(c, NodeTypeNullLiteral);
return trans_create_node_unsigned(c, 0);
case CK_Dependent:
emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent");
return nullptr;
@ -2425,7 +2439,8 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *
case BuiltinType::Float16:
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false));
case BuiltinType::NullPtr:
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq,
trans_create_node_unsigned(c, 0));
case BuiltinType::Void:
case BuiltinType::Half:
@ -2510,7 +2525,8 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *
break;
}
case Type::Pointer:
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq,
trans_create_node_unsigned(c, 0));
case Type::Typedef:
{
@ -4568,7 +4584,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t
} else if (first_tok->id == CTokIdAsterisk) {
*tok_i += 1;
node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown);
node = trans_create_node_ptr_type(c, false, false, node, PtrLenC);
} else {
return node;
}

View File

@ -236,6 +236,9 @@ pub fn formatType(
const casted_value = ([]const u8)(value);
return output(context, casted_value);
},
builtin.TypeInfo.Pointer.Size.C => {
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
},
},
builtin.TypeId.Array => |info| {
if (info.child == u8) {

View File

@ -496,6 +496,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
builtin.TypeId.Pointer => |info| switch (info.size) {
builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto hash C pointers"),
builtin.TypeInfo.Pointer.Size.Slice => {
const interval = std.math.max(1, key.len / 256);
var i: usize = 0;
@ -543,6 +544,7 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Pointer => |info| switch (info.size) {
builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto eql for C pointers"),
builtin.TypeInfo.Pointer.Size.Slice => {
if (a.len != b.len) return false;
for (a) |a_item, i| {

View File

@ -463,13 +463,16 @@ pub fn eql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Pointer => {
const info = @typeInfo(T).Pointer;
switch (info.size) {
builtin.TypeInfo.Pointer.Size.One, builtin.TypeInfo.Pointer.Size.Many => return a == b,
builtin.TypeInfo.Pointer.Size.One,
builtin.TypeInfo.Pointer.Size.Many,
builtin.TypeInfo.Pointer.Size.C,
=> return a == b,
builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
}
},
builtin.TypeId.Optional => {
if(a == null and b == null) return true;
if(a == null or b == null) return false;
if (a == null and b == null) return true;
if (a == null or b == null) return false;
return eql(a.?, b.?);
},
else => return a == b,

View File

@ -665,7 +665,7 @@ pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize {
pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
const ptr_result = c.mmap(
@ptrCast(*c_void, address),
@ptrCast(?*c_void, address),
length,
@bitCast(c_int, @intCast(c_uint, prot)),
@bitCast(c_int, c_uint(flags)),

View File

@ -65,7 +65,7 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
}
},
builtin.TypeInfo.Pointer.Size.Slice => {
builtin.TypeInfo.Pointer.Size.Slice => {
if (actual.ptr != expected.ptr) {
std.debug.panic("expected slice ptr {}, found {}", expected.ptr, actual.ptr);
}
@ -118,7 +118,6 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
}
}
},
}
}

View File

@ -3525,7 +3525,12 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op {
Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} },
Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} },
Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddressOf = void{} },
Token.Id.Asterisk, Token.Id.AsteriskAsterisk, Token.Id.BracketStarBracket => ast.Node.PrefixOp.Op{
Token.Id.Asterisk,
Token.Id.AsteriskAsterisk,
Token.Id.BracketStarBracket,
Token.Id.BracketStarCBracket,
=> ast.Node.PrefixOp.Op{
.PtrType = ast.Node.PrefixOp.PtrInfo{
.align_info = null,
.const_token = null,

View File

@ -1,3 +1,10 @@
test "zig fmt: C pointers" {
try testCanonical(
\\const Ptr = [*c]i32;
\\
);
}
test "zig fmt: threadlocal" {
try testCanonical(
\\threadlocal var x: i32 = 1234;

View File

@ -141,6 +141,7 @@ pub const Token = struct {
LineComment,
DocComment,
BracketStarBracket,
BracketStarCBracket,
ShebangLine,
Keyword_align,
Keyword_and,
@ -279,6 +280,7 @@ pub const Tokenizer = struct {
SawAtSign,
LBracket,
LBracketStar,
LBracketStarC,
};
pub fn next(self: *Tokenizer) Token {
@ -456,6 +458,9 @@ pub const Tokenizer = struct {
},
State.LBracketStar => switch (c) {
'c' => {
state = State.LBracketStarC;
},
']' => {
result.id = Token.Id.BracketStarBracket;
self.index += 1;
@ -467,6 +472,18 @@ pub const Tokenizer = struct {
},
},
State.LBracketStarC => switch (c) {
']' => {
result.id = Token.Id.BracketStarCBracket;
self.index += 1;
break;
},
else => {
result.id = Token.Id.Invalid;
break;
},
},
State.Ampersand => switch (c) {
'=' => {
result.id = Token.Id.AmpersandEqual;
@ -1035,6 +1052,7 @@ pub const Tokenizer = struct {
State.CharLiteralEnd,
State.StringLiteralBackslash,
State.LBracketStar,
State.LBracketStarC,
=> {
result.id = Token.Id.Invalid;
},
@ -1169,12 +1187,15 @@ test "tokenizer" {
testTokenize("test", []Token.Id{Token.Id.Keyword_test});
}
test "tokenizer - unknown length pointer" {
test "tokenizer - unknown length pointer and then c pointer" {
testTokenize(
\\[*]u8
\\[*c]u8
, []Token.Id{
Token.Id.BracketStarBracket,
Token.Id.Identifier,
Token.Id.BracketStarCBracket,
Token.Id.Identifier,
});
}

View File

@ -1,13 +1,149 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.addTest(
"C pointer to c_void",
\\export fn a() void {
\\ var x: *c_void = undefined;
\\ var y: [*c]c_void = x;
\\}
,
".tmp_source.zig:3:12: error: C pointers cannot point opaque types",
);
cases.addTest(
"directly embedding opaque type in struct and union",
\\const O = @OpaqueType();
\\const Foo = struct {
\\ o: O,
\\};
\\const Bar = union {
\\ One: i32,
\\ Two: O,
\\};
\\export fn a() void {
\\ var foo: Foo = undefined;
\\}
\\export fn b() void {
\\ var bar: Bar = undefined;
\\}
,
".tmp_source.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs",
".tmp_source.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions",
);
cases.addTest(
"implicit cast between C pointer and Zig pointer - bad const/align/child",
\\export fn a() void {
\\ var x: [*c]u8 = undefined;
\\ var y: *align(4) u8 = x;
\\}
\\export fn b() void {
\\ var x: [*c]const u8 = undefined;
\\ var y: *u8 = x;
\\}
\\export fn c() void {
\\ var x: [*c]u8 = undefined;
\\ var y: *u32 = x;
\\}
\\export fn d() void {
\\ var y: *align(1) u32 = undefined;
\\ var x: [*c]u32 = y;
\\}
\\export fn e() void {
\\ var y: *const u8 = undefined;
\\ var x: [*c]u8 = y;
\\}
\\export fn f() void {
\\ var y: *u8 = undefined;
\\ var x: [*c]u32 = y;
\\}
,
".tmp_source.zig:3:27: error: cast increases pointer alignment",
".tmp_source.zig:7:18: error: cast discards const qualifier",
".tmp_source.zig:11:19: error: expected type '*u32', found '[*c]u8'",
".tmp_source.zig:11:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32'",
".tmp_source.zig:15:22: error: cast increases pointer alignment",
".tmp_source.zig:19:21: error: cast discards const qualifier",
".tmp_source.zig:23:22: error: expected type '[*c]u32', found '*u8'",
);
cases.addTest(
"implicit casting null c pointer to zig pointer",
\\comptime {
\\ var c_ptr: [*c]u8 = 0;
\\ var zig_ptr: *u8 = c_ptr;
\\}
,
".tmp_source.zig:3:24: error: null pointer casted to type '*u8'",
);
cases.addTest(
"implicit casting undefined c pointer to zig pointer",
\\comptime {
\\ var c_ptr: [*c]u8 = undefined;
\\ var zig_ptr: *u8 = c_ptr;
\\}
,
".tmp_source.zig:3:24: error: use of undefined value here causes undefined behavior",
);
cases.addTest(
"implicit casting C pointers which would mess up null semantics",
\\export fn entry() void {
\\ var slice: []const u8 = "aoeu";
\\ const opt_many_ptr: [*]const u8 = slice.ptr;
\\ var ptr_opt_many_ptr = &opt_many_ptr;
\\ var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
\\ ptr_opt_many_ptr = c_ptr;
\\}
\\export fn entry2() void {
\\ var buf: [4]u8 = "aoeu";
\\ var slice: []u8 = &buf;
\\ var opt_many_ptr: [*]u8 = slice.ptr;
\\ var ptr_opt_many_ptr = &opt_many_ptr;
\\ var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr;
\\}
,
".tmp_source.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'",
".tmp_source.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'",
".tmp_source.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'",
".tmp_source.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'",
".tmp_source.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'",
".tmp_source.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'",
);
cases.addTest(
"implicit casting too big integers to C pointers",
\\export fn a() void {
\\ var ptr: [*c]u8 = (1 << 64) + 1;
\\}
\\export fn b() void {
\\ var x: @IntType(false, 65) = 0x1234;
\\ var ptr: [*c]u8 = x;
\\}
,
".tmp_source.zig:2:33: error: integer value 71615590737044764481 cannot be implicitly casted to type 'usize'",
".tmp_source.zig:6:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'",
);
cases.addTest(
"C pointer pointing to non C ABI compatible type or has align attr",
\\const Foo = struct {};
\\export fn a() void {
\\ const T = [*c]Foo;
\\}
,
".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'",
);
cases.addTest(
"@truncate undefined value",
\\export fn entry() void {
\\ var z = @truncate(u8, u16(undefined));
\\}
,
".tmp_source.zig:2:30: error: use of undefined value",
".tmp_source.zig:2:30: error: use of undefined value here causes undefined behavior",
);
cases.addTest(
@ -368,7 +504,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ f(i32);
\\}
,
".tmp_source.zig:4:5: error: use of undefined value",
".tmp_source.zig:4:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -768,7 +904,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ command.exec();
\\}
,
".tmp_source.zig:6:12: error: use of undefined value",
".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -781,7 +917,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ command.exec();
\\}
,
".tmp_source.zig:6:12: error: use of undefined value",
".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2752,7 +2888,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\
\\export fn entry() usize { return @sizeOf(@typeOf(x)); }
,
".tmp_source.zig:1:15: error: use of undefined value",
".tmp_source.zig:1:15: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2762,7 +2898,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a / a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2772,7 +2908,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a /= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2782,7 +2918,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a % a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2792,7 +2928,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a %= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2802,7 +2938,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a + a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2812,7 +2948,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a += a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2822,7 +2958,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a +% a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2832,7 +2968,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a +%= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2842,7 +2978,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a - a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2852,7 +2988,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a -= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2862,7 +2998,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a -% a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2872,7 +3008,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a -%= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2882,7 +3018,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a * a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2892,7 +3028,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a *= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2902,7 +3038,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a *% a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2912,7 +3048,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a *%= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2922,7 +3058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a << 2;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2932,7 +3068,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a <<= 2;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2942,7 +3078,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a >> 2;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2952,7 +3088,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a >>= 2;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2962,7 +3098,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a & a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2972,7 +3108,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a &= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2982,7 +3118,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a | a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -2992,7 +3128,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a |= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3002,7 +3138,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a ^ a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3012,7 +3148,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a ^= a;
\\}
,
".tmp_source.zig:3:5: error: use of undefined value",
".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3022,7 +3158,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a == a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3032,7 +3168,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a != a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3042,7 +3178,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a > a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3052,7 +3188,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a >= a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3062,7 +3198,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a < a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3072,7 +3208,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a <= a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3082,7 +3218,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a and a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3092,7 +3228,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a or a;
\\}
,
".tmp_source.zig:3:9: error: use of undefined value",
".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3102,7 +3238,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = -a;
\\}
,
".tmp_source.zig:3:10: error: use of undefined value",
".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3112,7 +3248,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = -%a;
\\}
,
".tmp_source.zig:3:11: error: use of undefined value",
".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3122,7 +3258,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = ~a;
\\}
,
".tmp_source.zig:3:10: error: use of undefined value",
".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3132,7 +3268,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = !a;
\\}
,
".tmp_source.zig:3:10: error: use of undefined value",
".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3142,7 +3278,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a orelse false;
\\}
,
".tmp_source.zig:3:11: error: use of undefined value",
".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
);
cases.add(
@ -3152,7 +3288,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a catch |err| false;
\\}
,
".tmp_source.zig:3:11: error: use of undefined value",
".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
);
cases.add(

View File

@ -1,6 +1,16 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
cases.addRuntimeSafety("pointer casting null to non-optional pointer",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() void {
\\ var c_ptr: [*c]u8 = 0;
\\ var zig_ptr: *u8 = c_ptr;
\\}
);
cases.addRuntimeSafety("@intToEnum - no matching tag value",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);

View File

@ -1,5 +1,6 @@
const std = @import("std");
const expect = std.testing.expect;
const expectError = std.testing.expectError;
test "dereference pointer" {
comptime testDerefPtr();
@ -42,3 +43,84 @@ test "double pointer parsing" {
fn PtrOf(comptime T: type) type {
return *T;
}
test "assigning integer to C pointer" {
var x: i32 = 0;
var ptr: [*c]u8 = 0;
var ptr2: [*c]u8 = x;
}
test "implicit cast single item pointer to C pointer and back" {
var y: u8 = 11;
var x: [*c]u8 = &y;
var z: *u8 = x;
z.* += 1;
expect(y == 12);
}
test "C pointer comparison and arithmetic" {
const S = struct {
fn doTheTest() void {
var one: usize = 1;
var ptr1: [*c]u32 = 0;
var ptr2 = ptr1 + 10;
expect(ptr1 == 0);
expect(ptr1 >= 0);
expect(ptr1 <= 0);
expect(ptr1 < 1);
expect(ptr1 < one);
expect(1 > ptr1);
expect(one > ptr1);
expect(ptr1 < ptr2);
expect(ptr2 > ptr1);
expect(ptr2 >= 40);
expect(ptr2 == 40);
expect(ptr2 <= 40);
ptr2 -= 10;
expect(ptr1 == ptr2);
}
};
S.doTheTest();
comptime S.doTheTest();
}
test "peer type resolution with C pointers" {
var ptr_one: *u8 = undefined;
var ptr_many: [*]u8 = undefined;
var ptr_c: [*c]u8 = undefined;
var t = true;
var x1 = if (t) ptr_one else ptr_c;
var x2 = if (t) ptr_many else ptr_c;
var x3 = if (t) ptr_c else ptr_one;
var x4 = if (t) ptr_c else ptr_many;
expect(@typeOf(x1) == [*c]u8);
expect(@typeOf(x2) == [*c]u8);
expect(@typeOf(x3) == [*c]u8);
expect(@typeOf(x4) == [*c]u8);
}
test "implicit casting between C pointer and optional non-C pointer" {
var slice: []const u8 = "aoeu";
const opt_many_ptr: ?[*]const u8 = slice.ptr;
var ptr_opt_many_ptr = &opt_many_ptr;
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
expect(c_ptr.*.* == 'a');
ptr_opt_many_ptr = c_ptr;
expect(ptr_opt_many_ptr.*.?[1] == 'o');
}
test "implicit cast error unions with non-optional to optional pointer" {
const S = struct {
fn doTheTest() void {
expectError(error.Fail, foo());
}
fn foo() anyerror!?*u8 {
return bar() orelse error.Fail;
}
fn bar() ?*u8 {
return null;
}
};
S.doTheTest();
comptime S.doTheTest();
}

View File

@ -61,6 +61,21 @@ fn testUnknownLenPtr() void {
expect(u32_ptr_info.Pointer.child == f64);
}
test "type info: C pointer type info" {
testCPtr();
comptime testCPtr();
}
fn testCPtr() void {
const ptr_info = @typeInfo([*c]align(4) const i8);
expect(TypeId(ptr_info) == TypeId.Pointer);
expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C);
expect(ptr_info.Pointer.is_const);
expect(!ptr_info.Pointer.is_volatile);
expect(ptr_info.Pointer.alignment == 4);
expect(ptr_info.Pointer.child == i8);
}
test "type info: slice type info" {
testSlice();
comptime testSlice();

View File

@ -117,11 +117,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
,
\\pub const struct_Foo = extern struct {
\\ a: ?[*]Foo,
\\ a: [*c]Foo,
\\};
\\pub const Foo = struct_Foo;
\\pub const struct_Bar = extern struct {
\\ a: ?[*]Foo,
\\ a: [*c]Foo,
\\};
);
@ -213,7 +213,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\const struct_Foo = extern struct {
\\ x: c_int,
\\ y: ?[*]u8,
\\ y: [*c]u8,
\\};
,
\\pub const Foo = struct_Foo;
@ -244,7 +244,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const BarB = enum_Bar.B;
,
\\pub extern fn func(a: ?[*]struct_Foo, b: ?[*](?[*]enum_Bar)) void;
\\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void;
,
\\pub const Foo = struct_Foo;
,
@ -254,7 +254,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("constant size array",
\\void func(int array[20]);
,
\\pub extern fn func(array: ?[*]c_int) void;
\\pub extern fn func(array: [*c]c_int) void;
);
cases.add("self referential struct with function pointer",
@ -263,7 +263,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
,
\\pub const struct_Foo = extern struct {
\\ derp: ?extern fn(?[*]struct_Foo) void,
\\ derp: ?extern fn([*c]struct_Foo) void,
\\};
,
\\pub const Foo = struct_Foo;
@ -322,11 +322,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
,
\\pub const struct_Bar = extern struct {
\\ next: ?[*]struct_Foo,
\\ next: [*c]struct_Foo,
\\};
,
\\pub const struct_Foo = extern struct {
\\ next: ?[*]struct_Bar,
\\ next: [*c]struct_Bar,
\\};
);
@ -610,11 +610,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0;
\\ if ((b != 0) and (c != null)) return 1;
\\ if ((a != 0) and (c != null)) return 2;
\\ if ((b != 0) and (c != 0)) return 1;
\\ if ((a != 0) and (c != 0)) return 2;
\\ if ((a != 0) or (b != 0)) return 3;
\\ if ((b != 0) or (c != null)) return 4;
\\ if ((a != 0) or (c != null)) return 5;
\\ if ((b != 0) or (c != 0)) return 4;
\\ if ((a != 0) or (c != 0)) return 5;
\\ return 6;
\\}
);
@ -710,7 +710,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const struct_Foo = extern struct {
\\ field: c_int,
\\};
\\pub export fn read_field(foo: ?[*]struct_Foo) c_int {
\\pub export fn read_field(foo: [*c]struct_Foo) c_int {
\\ return foo.?.field;
\\}
);
@ -756,7 +756,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return x;
\\}
,
\\pub export fn foo(x: ?[*]c_ushort) ?*c_void {
\\pub export fn foo(x: [*c]c_ushort) ?*c_void {
\\ return @ptrCast(?*c_void, x);
\\}
);
@ -777,8 +777,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 0;
\\}
,
\\pub export fn foo() ?[*]c_int {
\\ return null;
\\pub export fn foo() [*c]c_int {
\\ return 0;
\\}
);
@ -1086,7 +1086,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ *x = 1;
\\}
,
\\pub export fn foo(x: ?[*]c_int) void {
\\pub export fn foo(x: [*c]c_int) void {
\\ x.?.* = 1;
\\}
);
@ -1114,7 +1114,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub fn foo() c_int {
\\ var x: c_int = 1234;
\\ var ptr: ?[*]c_int = &x;
\\ var ptr: [*c]c_int = &x;
\\ return ptr.?.*;
\\}
);
@ -1124,7 +1124,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return "bar";
\\}
,
\\pub fn foo() ?[*]const u8 {
\\pub fn foo() [*c]const u8 {
\\ return c"bar";
\\}
);
@ -1253,8 +1253,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return (float *)a;
\\}
,
\\fn ptrcast(a: ?[*]c_int) ?[*]f32 {
\\ return @ptrCast(?[*]f32, a);
\\fn ptrcast(a: [*c]c_int) [*c]f32 {
\\ return @ptrCast([*c]f32, a);
\\}
);
@ -1280,7 +1280,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return !(a == 0);
\\ return !(a != 0);
\\ return !(b != 0);
\\ return !(c != null);
\\ return !(c != 0);
\\}
);
@ -1297,7 +1297,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("const ptr initializer",
\\static const char *v0 = "0.0.0";
,
\\pub var v0: ?[*]const u8 = c"0.0.0";
\\pub var v0: [*c]const u8 = c"0.0.0";
);
cases.add("static incomplete array inside function",
@ -1306,17 +1306,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
,
\\pub fn foo() void {
\\ const v2: [*]const u8 = c"2.2.2";
\\ const v2: [*c]const u8 = c"2.2.2";
\\}
);
cases.add("macro pointer cast",
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
,
\\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*]NRF_GPIO_Type)(NRF_GPIO_BASE);
\\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*c]NRF_GPIO_Type)(NRF_GPIO_BASE);
);
cases.add("if on none bool",
cases.add("if on non-bool",
\\enum SomeEnum { A, B, C };
\\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
\\ if (a) return 0;
@ -1337,13 +1337,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
\\ if (a != 0) return 0;
\\ if (b != 0) return 1;
\\ if (c != null) return 2;
\\ if (c != 0) return 2;
\\ if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3;
\\ return 4;
\\}
);
cases.add("while on none bool",
cases.add("while on non-bool",
\\int while_none_bool(int a, float b, void *c) {
\\ while (a) return 0;
\\ while (b) return 1;
@ -1354,12 +1354,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != null) return 2;
\\ while (c != 0) return 2;
\\ return 3;
\\}
);
cases.add("for on none bool",
cases.add("for on non-bool",
\\int for_none_bool(int a, float b, void *c) {
\\ for (;a;) return 0;
\\ for (;b;) return 1;
@ -1370,7 +1370,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != null) return 2;
\\ while (c != 0) return 2;
\\ return 3;
\\}
);