fix optional-presence comparison
This commit is contained in:
@@ -2730,8 +2730,9 @@ infer_call_comptime_values :: proc(
|
||||
for value_bound in bound {
|
||||
all_bound = all_bound && value_bound
|
||||
}
|
||||
// Concrete arguments bind first. Numeric constants are contextual and therefore
|
||||
// only contribute their default type after stronger evidence has had a chance.
|
||||
// Concrete arguments bind first. Numeric constants and `none` are contextual and
|
||||
// therefore only contribute after stronger evidence has had a chance to bind the
|
||||
// parameter type.
|
||||
weak_passes := [2]bool{false, true}
|
||||
for weak in weak_passes {
|
||||
for arg_id, source_index in args {
|
||||
@@ -2742,7 +2743,9 @@ infer_call_comptime_values :: proc(
|
||||
if param_index < 0 || function.params[param_index].comptime_value {
|
||||
continue
|
||||
}
|
||||
is_weak := is_numeric_constant_expr(checker, arg_id)
|
||||
arg_expr := checker.ast_module.exprs[arg_id]
|
||||
is_none := arg_expr.kind == .None
|
||||
is_weak := is_numeric_constant_expr(checker, arg_id) || is_none
|
||||
if is_weak != weak {
|
||||
continue
|
||||
}
|
||||
@@ -2753,7 +2756,6 @@ infer_call_comptime_values :: proc(
|
||||
// keyed record must be checked against the specialized parameter type.
|
||||
// Its provisional structural type intentionally contains only the supplied
|
||||
// fields, so comparing that type here would reject omitted defaulted fields.
|
||||
arg_expr := checker.ast_module.exprs[arg_id]
|
||||
if all_bound && arg_expr.kind == .Struct_Literal && !arg_expr.tuple && !symbol.is_valid(arg_expr.name) {
|
||||
continue
|
||||
}
|
||||
@@ -2766,9 +2768,22 @@ infer_call_comptime_values :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
actual := actual_args[param_index]
|
||||
if is_none {
|
||||
previous := checker.current_comptime_values
|
||||
checker.current_comptime_values = values
|
||||
contextual := type_from_syntax(
|
||||
checker, function.params[param_index].type, function.pkg, function.file,
|
||||
)
|
||||
checker.current_comptime_values = previous
|
||||
if types.is_optional(contextual, &checker.module.types) {
|
||||
actual = contextual
|
||||
actual_args[param_index] = contextual
|
||||
}
|
||||
}
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.params[param_index].type,
|
||||
actual_args[param_index], values, bound,
|
||||
actual, values, bound,
|
||||
checker.ast_module.exprs[arg_id].span, diagnose,
|
||||
) && matched
|
||||
}
|
||||
@@ -4482,8 +4497,18 @@ infer_compound_expr :: proc(
|
||||
right := infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types, types.U64)
|
||||
return left if types.is_concrete_integer(left) && types.is_unsigned(right, checker.target) else types.INVALID
|
||||
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
|
||||
left_expr := checker.ast_module.exprs[expr.left]
|
||||
right_expr := checker.ast_module.exprs[expr.right]
|
||||
if left_expr.kind == .None && right_expr.kind != .None {
|
||||
right := infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, right)
|
||||
} else if right_expr.kind == .None && left_expr.kind != .None {
|
||||
left := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types, left)
|
||||
} else {
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
|
||||
}
|
||||
return types.BOOL
|
||||
case .Range:
|
||||
left := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
@@ -4519,7 +4544,7 @@ infer_compound_expr :: proc(
|
||||
}
|
||||
return types.array(store, element, u64(len(expr.args)), false)
|
||||
case .None:
|
||||
return types.INVALID
|
||||
return expected if types.is_optional(expected, store) else types.INVALID
|
||||
case .Undefined:
|
||||
return types.INVALID
|
||||
case .Enum_Literal:
|
||||
@@ -8124,8 +8149,9 @@ build_compound_expr :: proc(
|
||||
left=left, right=right, target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
|
||||
// Contextualize a bare integer-literal operand to the other operand's type
|
||||
// so comparisons like `count > 0` or `0 < count` type-check.
|
||||
// Contextualize literals whose type comes from their peer. This covers
|
||||
// integer and enum literals as well as optional presence tests such as
|
||||
// `value == none` and `none != value`.
|
||||
left_const := eval_constant(checker, expr.left)
|
||||
right_const := eval_constant(checker, expr.right)
|
||||
left, right: hir.Expr_Id
|
||||
@@ -8133,7 +8159,13 @@ build_compound_expr :: proc(
|
||||
right_expr := checker.ast_module.exprs[expr.right]
|
||||
left_numeric_const := left_const.kind == .Value || is_float_constant_expr(checker, expr.left)
|
||||
right_numeric_const := right_const.kind == .Value || is_float_constant_expr(checker, expr.right)
|
||||
if right_expr.kind == .Enum_Literal && left_expr.kind != .Enum_Literal {
|
||||
if right_expr.kind == .None && left_expr.kind != .None {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, checker.module.exprs[left].type, pkg, file)
|
||||
} else if left_expr.kind == .None && right_expr.kind != .None {
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
|
||||
} else if right_expr.kind == .Enum_Literal && left_expr.kind != .Enum_Literal {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, checker.module.exprs[left].type, pkg, file)
|
||||
} else if left_expr.kind == .Enum_Literal && right_expr.kind != .Enum_Literal {
|
||||
@@ -8157,7 +8189,14 @@ build_compound_expr :: proc(
|
||||
left_type := checker.module.exprs[left].type
|
||||
right_type := checker.module.exprs[right].type
|
||||
operand_type := types.INVALID
|
||||
if types.is_enum(left_type, store) || types.is_enum(right_type, store) {
|
||||
if left_expr.kind == .None || right_expr.kind == .None {
|
||||
if expr.kind != .Eq && expr.kind != .Ne ||
|
||||
!types.is_optional(left_type, store) || !types.equal(left_type, right_type) {
|
||||
id := source.add(checker.diagnostics, expr.span, "'none' only supports '==' and '!=' with an optional value")
|
||||
return invalid_hir_expr(checker, expr.span, id, types.BOOL)
|
||||
}
|
||||
operand_type = left_type
|
||||
} else if types.is_enum(left_type, store) || types.is_enum(right_type, store) {
|
||||
if !types.equal(left_type, right_type) || (expr.kind != .Eq && expr.kind != .Ne) {
|
||||
id := source.add(checker.diagnostics, expr.span, "enum values only support '==' and '!=' with the same enum type")
|
||||
return invalid_hir_expr(checker, expr.span, id, types.BOOL)
|
||||
|
||||
Reference in New Issue
Block a user