Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions vlib/v/gen/c/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,66 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.expr(ast.Expr(node.right))
g.write(')')
}
} else if has_defined_eq_operator && left_is_option && right_is_option {
// `?T == ?T` where the base type `T` has a defined `==` (e.g. string, or a
// struct with an operator overload). Compare option-awarely: both none -> equal;
// differing none-ness -> not equal; otherwise compare the unwrapped values using
// the base type's `==`. (Without this, the eq function name was derived from the
// option-wrapped type, producing an undefined `_option_T__eq`.)
old_inside_opt_or_res := g.inside_opt_or_res
g.inside_opt_or_res = true
inside_and_rhs := g.infix_left_var_name.len > 0
mut lv := ''
mut rv := ''
if inside_and_rhs {
lv = g.expr_string(node.left)
rv = g.expr_string(node.right)
} else {
left_tmp := g.expr_to_ctemp_before_stmt(node.left, left_type)
right_tmp := g.expr_to_ctemp_before_stmt(node.right, right_type)
lv = left_tmp.name
rv = right_tmp.name
}
mut method_name := ''
if left.sym.kind == .struct && (left.sym.info as ast.Struct).generic_types.len > 0 {
concrete_types := (left.sym.info as ast.Struct).concrete_types
method_name = '${left.sym.cname}__eq'
if left.unaliased_sym.is_builtin() {
method_name = 'builtin__${method_name}'
}
method_name = g.generic_fn_name(concrete_types, method_name)
} else {
mut mn := if has_alias_eq_op_overload {
g.styp(left.typ.clear_flag(.option).set_nr_muls(0))
} else {
g.styp(left.unaliased.clear_flag(.option).set_nr_muls(0))
}
mut is_builtin_or_alias_to_builtin := left.sym.is_builtin()
if !has_alias_eq_op_overload && !is_builtin_or_alias_to_builtin
&& left.sym.info is ast.Alias {
parent_sym := g.table.sym((left.sym.info as ast.Alias).parent_type)
is_builtin_or_alias_to_builtin = parent_sym.is_builtin()
}
if is_builtin_or_alias_to_builtin {
mn = 'builtin__${mn}'
}
method_name = '${mn}__eq'
}
base_styp := g.base_type(left_type)
if node.op == .eq {
g.write('(')
} else {
g.write('!(')
}
g.write('(${lv}.state == 2 && ${rv}.state == 2) || ')
g.write('(${lv}.state == ${rv}.state && ${lv}.state != 2 && ')
if eq_operator_expects_ptr {
g.write('${method_name}((${base_styp}*)&${lv}.data, (${base_styp}*)&${rv}.data)')
} else {
g.write('${method_name}(*(${base_styp}*)&${lv}.data, *(${base_styp}*)&${rv}.data)')
}
g.write('))')
g.inside_opt_or_res = old_inside_opt_or_res
} else if has_defined_eq_operator {
if node.op == .ne {
g.write('!')
Expand Down
25 changes: 18 additions & 7 deletions vlib/v3/gen/c/cleanc.v
Original file line number Diff line number Diff line change
Expand Up @@ -4437,16 +4437,27 @@ fn (mut g FlatGen) gen_array_infix_eq(node flat.Node, lhs_id flat.NodeId, rhs_id
}

fn (mut g FlatGen) gen_fixed_array_infix_eq(node flat.Node, lhs_id flat.NodeId, rhs_id flat.NodeId, lhs_type types.Type, rhs_type types.Type) bool {
lhs_fixed := array_fixed_type(types.unwrap_pointer(lhs_type)) or { types.ArrayFixed{} }
rhs_fixed := array_fixed_type(types.unwrap_pointer(rhs_type)) or { types.ArrayFixed{} }
lhs_is_fixed := lhs_fixed.elem_type !is types.Void || lhs_fixed.len > 0
|| lhs_fixed.len_expr.len > 0
rhs_is_fixed := rhs_fixed.elem_type !is types.Void || rhs_fixed.len > 0
|| rhs_fixed.len_expr.len > 0
// Detect fixed-array operands by the optional's presence, not by inspecting a
// default-constructed `ArrayFixed{}` sentinel. The zero value of the `elem_type`
// sum-type field is not reliably `Void` across the self-host bootstrap, so the old
// `!is types.Void` check misfired for scalar comparisons (e.g. `(a.flags & 32) != 0`
// from `@[flag]` enum `.has()`), emitting a bogus `memcmp(..., sizeof(Array_fixed_int_0))`.
mut fixed := types.ArrayFixed{}
mut lhs_is_fixed := false
mut rhs_is_fixed := false
if f := array_fixed_type(types.unwrap_pointer(lhs_type)) {
fixed = f
lhs_is_fixed = true
}
if f := array_fixed_type(types.unwrap_pointer(rhs_type)) {
if !lhs_is_fixed {
fixed = f
}
rhs_is_fixed = true
}
if !lhs_is_fixed && !rhs_is_fixed {
return false
}
fixed := if lhs_is_fixed { lhs_fixed } else { rhs_fixed }
if !lhs_is_fixed && !g.expr_can_be_fixed_array_literal(lhs_id) {
return false
}
Expand Down
18 changes: 18 additions & 0 deletions vlib/v3/pref/pref.v
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,24 @@ pub fn file_has_incompatible_os_suffix(file string, current_os string) bool {
if os_name != 'solaris' && file.contains('_solaris.') {
return true
}
if os_name != 'qnx' && file.contains('_qnx.') {
return true
}
if os_name != 'serenity' && file.contains('_serenity.') {
return true
}
if os_name != 'plan9' && file.contains('_plan9.') {
return true
}
if os_name != 'vinix' && file.contains('_vinix.') {
return true
}
if os_name != 'haiku' && file.contains('_haiku.') {
return true
}
if os_name != 'android' && os_name != 'termux' && file.contains('_termux.') {
return true
}
if file.contains('.amd64.') || file.contains('_amd64.') || file.contains('.arm64.')
|| file.contains('_arm64.') {
return true
Expand Down
21 changes: 7 additions & 14 deletions vlib/v3/transform/monomorphize.v
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,13 @@ fn (mut t Transformer) collect_generic_fn_decls_for_erasure() map[string]Generic
cur_module = node.value
}
.fn_decl {
if !generic_fn_decl_needs_erasure_scan(node, t.a) {
// Use the same detector as the monomorphize path: a method whose
// only generic parameter comes from its receiver struct (e.g.
// `fn (t ThreadLocalStorage[T]) set(value T)`) has no method-level
// `generic_params` and no `generic` substring in its param types, so
// the old literal-substring scan missed it and cgen emitted a raw
// `_T_` template body calling erased helpers.
if !t.fn_decl_has_unresolved_generics(node, cur_module) {
continue
}
key := t.generic_fn_decl_key(node, cur_module)
Expand All @@ -156,19 +162,6 @@ fn (mut t Transformer) collect_generic_fn_decls_for_erasure() map[string]Generic
return decls
}

fn generic_fn_decl_needs_erasure_scan(node flat.Node, a &flat.FlatAst) bool {
if node.generic_params.len > 0 || node.typ.contains('generic') {
return true
}
for i in 0 .. node.children_count {
child := a.child_node(&node, i)
if child.kind == .param && child.typ.contains('generic') {
return true
}
}
return false
}

fn building_v_keeps_type_erased_generic_template(key string) bool {
return key in ['token.new_keywords_matcher_trie', 'sync.pool.PoolProcessor.work_on_items']
}
Expand Down
14 changes: 10 additions & 4 deletions vlib/v3/types/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -803,20 +803,26 @@ fn (mut tc TypeChecker) check_c_struct_redeclarations(a &flat.FlatAst) {
if !qname.starts_with('C.') {
continue
}
// Scope the check per module: a C struct name refers to a single
// external C type, but different modules may each mirror it with
// their own field view (e.g. `C.termios` in both `term` and
// `term.termios`). Only redeclarations within the same module are
// a genuine conflict.
key := '${tc.cur_module}|${qname}'
c_struct_sig := tc.c_struct_decl_signature(a, node)
if qname in c_struct_decl_sigs {
existing_sig := c_struct_decl_sigs[qname]
if key in c_struct_decl_sigs {
existing_sig := c_struct_decl_sigs[key]
if !c_struct_decl_signatures_compatible(existing_sig, c_struct_sig) {
tc.record_error_unfiltered(.duplicate_decl,
'cannot redeclare C struct `${qname}`', flat.NodeId(node_idx))
}
existing_fields := c_struct_decl_signature_field_count(existing_sig)
current_fields := c_struct_decl_signature_field_count(c_struct_sig)
if current_fields > existing_fields {
c_struct_decl_sigs[qname] = c_struct_sig
c_struct_decl_sigs[key] = c_struct_sig
}
} else {
c_struct_decl_sigs[qname] = c_struct_sig
c_struct_decl_sigs[key] = c_struct_sig
}
}
else {}
Expand Down
Loading