diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-10-26 13:46:27 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-10-26 13:46:27 -0700 |
| commit | c6b3d06535f4227541c13fe75da347a485abdb4f (patch) | |
| tree | 8f0a730e1dc7619168a81d2cd4521db1e7a1b4b6 /src/Sema.zig | |
| parent | 6df26a37d13d21be061a1cccd39dd17e46a81322 (diff) | |
| download | zig-c6b3d06535f4227541c13fe75da347a485abdb4f.tar.gz zig-c6b3d06535f4227541c13fe75da347a485abdb4f.zip | |
Sema: improved C pointers and casting
* C pointer types always have allowzero set to true but they omit the
word allowzero when printed.
* Implement coercion from C pointers to other pointers.
* Implement in-memory coercion for slices and pointer-like optionals.
* Make slicing a C pointer drop the allowzero bit.
* Value representation for pointer-like optionals is now allowed to use
pointer tag values in addition to the `opt_payload` tag.
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 169 |
1 files changed, 104 insertions, 65 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index dfe42e38ea..d61aa52be4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9125,7 +9125,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .pointee_type = elem_type, .@"addrspace" = .generic, .mutable = inst_data.is_mutable, - .@"allowzero" = inst_data.is_allowzero, + .@"allowzero" = inst_data.is_allowzero or inst_data.size == .C, .@"volatile" = inst_data.is_volatile, .size = inst_data.size, }); @@ -9185,7 +9185,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .bit_offset = bit_start, .host_size = bit_end, .mutable = inst_data.flags.is_mutable, - .@"allowzero" = inst_data.flags.is_allowzero, + .@"allowzero" = inst_data.flags.is_allowzero or inst_data.size == .C, .@"volatile" = inst_data.flags.is_volatile, .size = inst_data.size, }); @@ -12102,6 +12102,21 @@ fn coerce( } } + // coercion from C pointer + if (inst_ty.isCPtr()) src_c_ptr: { + // In this case we must add a safety check because the C pointer + // could be null. + const src_elem_ty = inst_ty.childType(); + const dest_is_mut = dest_info.mutable; + const dst_elem_type = dest_info.pointee_type; + switch (coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) { + .ok => {}, + .no_match => break :src_c_ptr, + } + // TODO add safety check for null pointer + return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); + } + // coercion to C pointer if (dest_info.size == .C) { switch (inst_ty.zigTypeTag()) { @@ -12262,84 +12277,107 @@ const InMemoryCoercionResult = enum { /// * sentinel-terminated pointers can coerce into `[*]` /// TODO improve this function to report recursive compile errors like it does in stage1. /// look at the function types_match_const_cast_only -fn coerceInMemoryAllowed(dest_ty: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult { - if (dest_ty.eql(src_type)) +fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult { + if (dest_ty.eql(src_ty)) return .ok; - if (dest_ty.zigTypeTag() == .Pointer and - src_type.zigTypeTag() == .Pointer) - { - const dest_info = dest_ty.ptrInfo().data; - const src_info = src_type.ptrInfo().data; - - const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target); - if (child == .no_match) { - return child; + // Pointers / Pointer-like Optionals + var dest_buf: Type.Payload.ElemType = undefined; + var src_buf: Type.Payload.ElemType = undefined; + if (dest_ty.ptrOrOptionalPtrTy(&dest_buf)) |dest_ptr_ty| { + if (src_ty.ptrOrOptionalPtrTy(&src_buf)) |src_ptr_ty| { + return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target); } + } - if (dest_info.@"addrspace" != src_info.@"addrspace") { - return .no_match; - } + // Slices + if (dest_ty.isSlice() and src_ty.isSlice()) { + return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); + } - const ok_sent = dest_info.sentinel == null or src_info.size == .C or - (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type)); - if (!ok_sent) { - return .no_match; - } + // TODO: arrays + // TODO: non-pointer-like optionals + // TODO: error unions + // TODO: error sets + // TODO: functions + // TODO: vectors - const ok_ptr_size = src_info.size == dest_info.size or - src_info.size == .C or dest_info.size == .C; - if (!ok_ptr_size) { - return .no_match; - } + return .no_match; +} - const ok_cv_qualifiers = - (src_info.mutable or !dest_info.mutable) and - (!src_info.@"volatile" or dest_info.@"volatile"); +fn coerceInMemoryAllowedPtrs( + dest_ty: Type, + src_ty: Type, + dest_ptr_ty: Type, + src_ptr_ty: Type, + dest_is_mut: bool, + target: std.Target, +) InMemoryCoercionResult { + const dest_info = dest_ptr_ty.ptrInfo().data; + const src_info = src_ptr_ty.ptrInfo().data; - if (!ok_cv_qualifiers) { - return .no_match; - } + const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target); + if (child == .no_match) { + return child; + } - const ok_allows_zero = (dest_info.@"allowzero" and - (src_info.@"allowzero" or !dest_is_mut)) or - (!dest_info.@"allowzero" and !src_info.@"allowzero"); - if (!ok_allows_zero) { - return .no_match; - } + if (dest_info.@"addrspace" != src_info.@"addrspace") { + return .no_match; + } - if (dest_ty.hasCodeGenBits() != src_type.hasCodeGenBits()) { - return .no_match; - } + const ok_sent = dest_info.sentinel == null or src_info.size == .C or + (src_info.sentinel != null and + dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type)); + if (!ok_sent) { + return .no_match; + } - if (src_info.host_size != dest_info.host_size or - src_info.bit_offset != dest_info.bit_offset) - { - return .no_match; - } + const ok_ptr_size = src_info.size == dest_info.size or + src_info.size == .C or dest_info.size == .C; + if (!ok_ptr_size) { + return .no_match; + } - // If both pointers have alignment 0, it means they both want ABI alignment. - // In this case, if they share the same child type, no need to resolve - // pointee type alignment. Otherwise both pointee types must have their alignment - // resolved and we compare the alignment numerically. - if (src_info.@"align" != 0 or dest_info.@"align" != 0 or - !dest_info.pointee_type.eql(src_info.pointee_type)) - { - const src_align = src_type.ptrAlignment(target); - const dest_align = dest_ty.ptrAlignment(target); + const ok_cv_qualifiers = + (src_info.mutable or !dest_info.mutable) and + (!src_info.@"volatile" or dest_info.@"volatile"); - if (dest_align > src_align) { - return .no_match; - } - } + if (!ok_cv_qualifiers) { + return .no_match; + } - return .ok; + const dest_allow_zero = dest_ty.ptrAllowsZero(); + const src_allow_zero = src_ty.ptrAllowsZero(); + + const ok_allows_zero = (dest_allow_zero and + (src_allow_zero or !dest_is_mut)) or + (!dest_allow_zero and !src_allow_zero); + if (!ok_allows_zero) { + return .no_match; } - // TODO: implement more of this function + if (src_info.host_size != dest_info.host_size or + src_info.bit_offset != dest_info.bit_offset) + { + return .no_match; + } - return .no_match; + // If both pointers have alignment 0, it means they both want ABI alignment. + // In this case, if they share the same child type, no need to resolve + // pointee type alignment. Otherwise both pointee types must have their alignment + // resolved and we compare the alignment numerically. + if (src_info.@"align" != 0 or dest_info.@"align" != 0 or + !dest_info.pointee_type.eql(src_info.pointee_type)) + { + const src_align = src_info.@"align"; + const dest_align = dest_info.@"align"; + + if (dest_align > src_align) { + return .no_match; + } + } + + return .ok; } fn coerceNum( @@ -13297,6 +13335,7 @@ fn analyzeSlice( const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; + const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize() != .C; if (opt_new_len_val) |new_len_val| { const new_len_int = new_len_val.toUnsignedInt(); @@ -13312,7 +13351,7 @@ fn analyzeSlice( .@"align" = new_ptr_ty_info.@"align", .@"addrspace" = new_ptr_ty_info.@"addrspace", .mutable = new_ptr_ty_info.mutable, - .@"allowzero" = new_ptr_ty_info.@"allowzero", + .@"allowzero" = new_allowzero, .@"volatile" = new_ptr_ty_info.@"volatile", .size = .One, }); @@ -13340,7 +13379,7 @@ fn analyzeSlice( .@"align" = new_ptr_ty_info.@"align", .@"addrspace" = new_ptr_ty_info.@"addrspace", .mutable = new_ptr_ty_info.mutable, - .@"allowzero" = new_ptr_ty_info.@"allowzero", + .@"allowzero" = new_allowzero, .@"volatile" = new_ptr_ty_info.@"volatile", .size = .Slice, }); |
