| ... | @@ -193,12 +193,12 @@ pub fn analyzeBody( | ... | @@ -193,12 +193,12 @@ pub fn analyzeBody( |
| 193 | .call_compile_time => try sema.zirCall(block, inst, .compile_time, false), | 193 | .call_compile_time => try sema.zirCall(block, inst, .compile_time, false), |
| 194 | .call_nosuspend => try sema.zirCall(block, inst, .no_async, false), | 194 | .call_nosuspend => try sema.zirCall(block, inst, .no_async, false), |
| 195 | .call_async => try sema.zirCall(block, inst, .async_kw, false), | 195 | .call_async => try sema.zirCall(block, inst, .async_kw, false), |
| 196 | .cmp_eq => try sema.zirCmp(block, inst, .eq), | | |
| 197 | .cmp_gt => try sema.zirCmp(block, inst, .gt), | | |
| 198 | .cmp_gte => try sema.zirCmp(block, inst, .gte), | | |
| 199 | .cmp_lt => try sema.zirCmp(block, inst, .lt), | 196 | .cmp_lt => try sema.zirCmp(block, inst, .lt), |
| 200 | .cmp_lte => try sema.zirCmp(block, inst, .lte), | 197 | .cmp_lte => try sema.zirCmp(block, inst, .lte), |
| 201 | .cmp_neq => try sema.zirCmp(block, inst, .neq), | 198 | .cmp_eq => try sema.zirCmpEq(block, inst, .eq, .cmp_eq), |
| | 199 | .cmp_gte => try sema.zirCmp(block, inst, .gte), |
| | 200 | .cmp_gt => try sema.zirCmp(block, inst, .gt), |
| | 201 | .cmp_neq => try sema.zirCmpEq(block, inst, .neq, .cmp_neq), |
| 202 | .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), | 202 | .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), |
| 203 | .decl_ref => try sema.zirDeclRef(block, inst), | 203 | .decl_ref => try sema.zirDeclRef(block, inst), |
| 204 | .decl_val => try sema.zirDeclVal(block, inst), | 204 | .decl_val => try sema.zirDeclVal(block, inst), |
| ... | @@ -5040,17 +5040,18 @@ fn zirAsm( | ... | @@ -5040,17 +5040,18 @@ fn zirAsm( |
| 5040 | return asm_air; | 5040 | return asm_air; |
| 5041 | } | 5041 | } |
| 5042 | | 5042 | |
| 5043 | fn zirCmp( | 5043 | /// Only called for equality operators. See also `zirCmp`. |
| | 5044 | fn zirCmpEq( |
| 5044 | sema: *Sema, | 5045 | sema: *Sema, |
| 5045 | block: *Scope.Block, | 5046 | block: *Scope.Block, |
| 5046 | inst: Zir.Inst.Index, | 5047 | inst: Zir.Inst.Index, |
| 5047 | op: std.math.CompareOperator, | 5048 | op: std.math.CompareOperator, |
| | 5049 | air_tag: Air.Inst.Tag, |
| 5048 | ) CompileError!Air.Inst.Ref { | 5050 | ) CompileError!Air.Inst.Ref { |
| 5049 | const tracy = trace(@src()); | 5051 | const tracy = trace(@src()); |
| 5050 | defer tracy.end(); | 5052 | defer tracy.end(); |
| 5051 | | 5053 | |
| 5052 | const mod = sema.mod; | 5054 | const mod = sema.mod; |
| 5053 | | | |
| 5054 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 5055 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5055 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | 5056 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 5056 | const src: LazySrcLoc = inst_data.src(); | 5057 | const src: LazySrcLoc = inst_data.src(); |
| ... | @@ -5059,73 +5060,65 @@ fn zirCmp( | ... | @@ -5059,73 +5060,65 @@ fn zirCmp( |
| 5059 | const lhs = sema.resolveInst(extra.lhs); | 5060 | const lhs = sema.resolveInst(extra.lhs); |
| 5060 | const rhs = sema.resolveInst(extra.rhs); | 5061 | const rhs = sema.resolveInst(extra.rhs); |
| 5061 | | 5062 | |
| 5062 | const is_equality_cmp = switch (op) { | | |
| 5063 | .eq, .neq => true, | | |
| 5064 | else => false, | | |
| 5065 | }; | | |
| 5066 | const lhs_ty = sema.typeOf(lhs); | 5063 | const lhs_ty = sema.typeOf(lhs); |
| 5067 | const rhs_ty = sema.typeOf(rhs); | 5064 | const rhs_ty = sema.typeOf(rhs); |
| 5068 | const lhs_ty_tag = lhs_ty.zigTypeTag(); | 5065 | const lhs_ty_tag = lhs_ty.zigTypeTag(); |
| 5069 | const rhs_ty_tag = rhs_ty.zigTypeTag(); | 5066 | const rhs_ty_tag = rhs_ty.zigTypeTag(); |
| 5070 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | 5067 | if (lhs_ty_tag == .Null and rhs_ty_tag == .Null) { |
| 5071 | // null == null, null != null | 5068 | // null == null, null != null |
| 5072 | if (op == .eq) { | 5069 | if (op == .eq) { |
| 5073 | return Air.Inst.Ref.bool_true; | 5070 | return Air.Inst.Ref.bool_true; |
| 5074 | } else { | 5071 | } else { |
| 5075 | return Air.Inst.Ref.bool_false; | 5072 | return Air.Inst.Ref.bool_false; |
| 5076 | } | 5073 | } |
| 5077 | } else if (is_equality_cmp and | 5074 | } |
| 5078 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | 5075 | if (((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or |
| 5079 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | 5076 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) |
| 5080 | { | 5077 | { |
| 5081 | // comparing null with optionals | 5078 | // comparing null with optionals |
| 5082 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | 5079 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; |
| 5083 | return sema.analyzeIsNull(block, src, opt_operand, op == .neq); | 5080 | return sema.analyzeIsNull(block, src, opt_operand, op == .neq); |
| 5084 | } else if (is_equality_cmp and | 5081 | } |
| 5085 | ((lhs_ty_tag == .Null and rhs_ty.isCPtr()) or (rhs_ty_tag == .Null and lhs_ty.isCPtr()))) | 5082 | if (((lhs_ty_tag == .Null and rhs_ty.isCPtr()) or (rhs_ty_tag == .Null and lhs_ty.isCPtr()))) { |
| 5086 | { | | |
| 5087 | return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{}); | 5083 | return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{}); |
| 5088 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | 5084 | } |
| | 5085 | if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { |
| 5089 | const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; | 5086 | const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; |
| 5090 | return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); | 5087 | return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); |
| 5091 | } else if (is_equality_cmp and | 5088 | } |
| 5092 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | 5089 | if (((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or |
| 5093 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | 5090 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) |
| 5094 | { | 5091 | { |
| 5095 | return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | 5092 | return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); |
| 5096 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | 5093 | } |
| 5097 | if (!is_equality_cmp) { | 5094 | if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { |
| 5098 | return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)}); | 5095 | const runtime_src: LazySrcLoc = src: { |
| 5099 | } | 5096 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lval| { |
| 5100 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lval| { | 5097 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rval| { |
| 5101 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rval| { | 5098 | if (lval.isUndef() or rval.isUndef()) { |
| 5102 | if (lval.isUndef() or rval.isUndef()) { | 5099 | return sema.addConstUndef(Type.initTag(.bool)); |
| 5103 | return sema.addConstUndef(Type.initTag(.bool)); | 5100 | } |
| 5104 | } | 5101 | // TODO optimisation opportunity: evaluate if mem.eql is faster with the names, |
| 5105 | // TODO optimisation opportunity: evaluate if mem.eql is faster with the names, | 5102 | // or calling to Module.getErrorValue to get the values and then compare them is |
| 5106 | // or calling to Module.getErrorValue to get the values and then compare them is | 5103 | // faster. |
| 5107 | // faster. | 5104 | const lhs_name = lval.castTag(.@"error").?.data.name; |
| 5108 | const lhs_name = lval.castTag(.@"error").?.data.name; | 5105 | const rhs_name = rval.castTag(.@"error").?.data.name; |
| 5109 | const rhs_name = rval.castTag(.@"error").?.data.name; | 5106 | if (mem.eql(u8, lhs_name, rhs_name) == (op == .eq)) { |
| 5110 | if (mem.eql(u8, lhs_name, rhs_name) == (op == .eq)) { | 5107 | return Air.Inst.Ref.bool_true; |
| 5111 | return Air.Inst.Ref.bool_true; | 5108 | } else { |
| | 5109 | return Air.Inst.Ref.bool_false; |
| | 5110 | } |
| 5112 | } else { | 5111 | } else { |
| 5113 | return Air.Inst.Ref.bool_false; | 5112 | break :src rhs_src; |
| 5114 | } | 5113 | } |
| | 5114 | } else { |
| | 5115 | break :src lhs_src; |
| 5115 | } | 5116 | } |
| 5116 | } | 5117 | }; |
| 5117 | try sema.requireRuntimeBlock(block, src); | 5118 | try sema.requireRuntimeBlock(block, runtime_src); |
| 5118 | const tag: Air.Inst.Tag = if (op == .eq) .cmp_eq else .cmp_neq; | 5119 | return block.addBinOp(air_tag, lhs, rhs); |
| 5119 | return block.addBinOp(tag, lhs, rhs); | 5120 | } |
| 5120 | } else if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) { | 5121 | if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { |
| 5121 | // This operation allows any combination of integer and float types, regardless of the | | |
| 5122 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | | |
| 5123 | // numeric types. | | |
| 5124 | return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); | | |
| 5125 | } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { | | |
| 5126 | if (!is_equality_cmp) { | | |
| 5127 | return mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)}); | | |
| 5128 | } | | |
| 5129 | const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); | 5122 | const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); |
| 5130 | const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); | 5123 | const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); |
| 5131 | if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) { | 5124 | if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) { |
| ... | @@ -5134,11 +5127,54 @@ fn zirCmp( | ... | @@ -5134,11 +5127,54 @@ fn zirCmp( |
| 5134 | return Air.Inst.Ref.bool_false; | 5127 | return Air.Inst.Ref.bool_false; |
| 5135 | } | 5128 | } |
| 5136 | } | 5129 | } |
| | 5130 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); |
| | 5131 | } |
| | 5132 | |
| | 5133 | /// Only called for non-equality operators. See also `zirCmpEq`. |
| | 5134 | fn zirCmp( |
| | 5135 | sema: *Sema, |
| | 5136 | block: *Scope.Block, |
| | 5137 | inst: Zir.Inst.Index, |
| | 5138 | op: std.math.CompareOperator, |
| | 5139 | ) CompileError!Air.Inst.Ref { |
| | 5140 | const tracy = trace(@src()); |
| | 5141 | defer tracy.end(); |
| 5137 | | 5142 | |
| | 5143 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| | 5144 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| | 5145 | const src: LazySrcLoc = inst_data.src(); |
| | 5146 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| | 5147 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| | 5148 | const lhs = sema.resolveInst(extra.lhs); |
| | 5149 | const rhs = sema.resolveInst(extra.rhs); |
| | 5150 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); |
| | 5151 | } |
| | 5152 | |
| | 5153 | fn analyzeCmp( |
| | 5154 | sema: *Sema, |
| | 5155 | block: *Scope.Block, |
| | 5156 | src: LazySrcLoc, |
| | 5157 | lhs: Air.Inst.Ref, |
| | 5158 | rhs: Air.Inst.Ref, |
| | 5159 | op: std.math.CompareOperator, |
| | 5160 | lhs_src: LazySrcLoc, |
| | 5161 | rhs_src: LazySrcLoc, |
| | 5162 | is_equality_cmp: bool, |
| | 5163 | ) CompileError!Air.Inst.Ref { |
| | 5164 | const lhs_ty = sema.typeOf(lhs); |
| | 5165 | const rhs_ty = sema.typeOf(rhs); |
| | 5166 | if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) { |
| | 5167 | // This operation allows any combination of integer and float types, regardless of the |
| | 5168 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for |
| | 5169 | // numeric types. |
| | 5170 | return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); |
| | 5171 | } |
| 5138 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; | 5172 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 5139 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions); | 5173 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions); |
| 5140 | if (!resolved_type.isSelfComparable(is_equality_cmp)) { | 5174 | if (!resolved_type.isSelfComparable(is_equality_cmp)) { |
| 5141 | return mod.fail(&block.base, src, "operator not allowed for type '{}'", .{resolved_type}); | 5175 | return sema.mod.fail(&block.base, src, "{s} operator not allowed for type '{}'", .{ |
| | 5176 | @tagName(op), resolved_type, |
| | 5177 | }); |
| 5142 | } | 5178 | } |
| 5143 | | 5179 | |
| 5144 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | 5180 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| ... | @@ -5146,19 +5182,31 @@ fn zirCmp( | ... | @@ -5146,19 +5182,31 @@ fn zirCmp( |
| 5146 | | 5182 | |
| 5147 | const runtime_src: LazySrcLoc = src: { | 5183 | const runtime_src: LazySrcLoc = src: { |
| 5148 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { | 5184 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { |
| | 5185 | if (lhs_val.isUndef()) return sema.addConstUndef(resolved_type); |
| 5149 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { | 5186 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { |
| 5150 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 5187 | if (rhs_val.isUndef()) return sema.addConstUndef(resolved_type); |
| 5151 | return sema.addConstUndef(resolved_type); | 5188 | |
| 5152 | } | | |
| 5153 | if (lhs_val.compare(op, rhs_val, resolved_type)) { | 5189 | if (lhs_val.compare(op, rhs_val, resolved_type)) { |
| 5154 | return Air.Inst.Ref.bool_true; | 5190 | return Air.Inst.Ref.bool_true; |
| 5155 | } else { | 5191 | } else { |
| 5156 | return Air.Inst.Ref.bool_false; | 5192 | return Air.Inst.Ref.bool_false; |
| 5157 | } | 5193 | } |
| 5158 | } else { | 5194 | } else { |
| | 5195 | if (resolved_type.zigTypeTag() == .Bool) { |
| | 5196 | // We can lower bool eq/neq more efficiently. |
| | 5197 | return sema.runtimeBoolCmp(block, op, casted_rhs, lhs_val.toBool(), rhs_src); |
| | 5198 | } |
| 5159 | break :src rhs_src; | 5199 | break :src rhs_src; |
| 5160 | } | 5200 | } |
| 5161 | } else { | 5201 | } else { |
| | 5202 | // For bools, we still check the other operand, because we can lower |
| | 5203 | // bool eq/neq more efficiently. |
| | 5204 | if (resolved_type.zigTypeTag() == .Bool) { |
| | 5205 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { |
| | 5206 | if (rhs_val.isUndef()) return sema.addConstUndef(resolved_type); |
| | 5207 | return sema.runtimeBoolCmp(block, op, casted_lhs, rhs_val.toBool(), lhs_src); |
| | 5208 | } |
| | 5209 | } |
| 5162 | break :src lhs_src; | 5210 | break :src lhs_src; |
| 5163 | } | 5211 | } |
| 5164 | }; | 5212 | }; |
| ... | @@ -5176,6 +5224,26 @@ fn zirCmp( | ... | @@ -5176,6 +5224,26 @@ fn zirCmp( |
| 5176 | return block.addBinOp(tag, casted_lhs, casted_rhs); | 5224 | return block.addBinOp(tag, casted_lhs, casted_rhs); |
| 5177 | } | 5225 | } |
| 5178 | | 5226 | |
| | 5227 | /// cmp_eq (x, false) => not(x) |
| | 5228 | /// cmp_eq (x, true ) => x |
| | 5229 | /// cmp_neq(x, false) => x |
| | 5230 | /// cmp_neq(x, true ) => not(x) |
| | 5231 | fn runtimeBoolCmp( |
| | 5232 | sema: *Sema, |
| | 5233 | block: *Scope.Block, |
| | 5234 | op: std.math.CompareOperator, |
| | 5235 | lhs: Air.Inst.Ref, |
| | 5236 | rhs: bool, |
| | 5237 | runtime_src: LazySrcLoc, |
| | 5238 | ) CompileError!Air.Inst.Ref { |
| | 5239 | if ((op == .neq) == rhs) { |
| | 5240 | try sema.requireRuntimeBlock(block, runtime_src); |
| | 5241 | return block.addTyOp(.not, Type.initTag(.bool), lhs); |
| | 5242 | } else { |
| | 5243 | return lhs; |
| | 5244 | } |
| | 5245 | } |
| | 5246 | |
| 5179 | fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 5247 | fn zirSizeOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5180 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 5248 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 5181 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 5249 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |