unsigned integer constraint (uint)
This commit is contained in:
@@ -501,7 +501,7 @@ write_type_label :: proc(checker: ^Checker, builder: ^strings.Builder, value: ty
|
||||
strings.write_string(builder, "enum")
|
||||
case .Alias, .Distinct, .Named:
|
||||
write_type_label(checker, builder, item.child)
|
||||
case .Invalid, .Void, .Anyopaque, .Int_Constraint, .Float_Constraint, .Range_Constraint, .Scalar:
|
||||
case .Invalid, .Void, .Anyopaque, .Int_Constraint, .Uint_Constraint, .Float_Constraint, .Range_Constraint, .Scalar:
|
||||
strings.write_string(builder, types.name(value))
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1052,26 @@ fits_i64 :: proc(value: i128) -> bool {
|
||||
return fits_signed_type(value, types.I64)
|
||||
}
|
||||
|
||||
fits_u64 :: proc(value: i128) -> bool {
|
||||
return value >= 0 && value <= i128(0xffff_ffff_ffff_ffff)
|
||||
}
|
||||
|
||||
constraint_integer_literal_type :: proc(constraint: types.Type, value: i128) -> types.Type {
|
||||
if constraint == types.UINT {
|
||||
return types.smallest_unsigned_for_literal(u64(value)) if fits_u64(value) else types.INVALID
|
||||
}
|
||||
return types.smallest_signed_for_literal(i64(value)) if fits_i64(value) else types.INVALID
|
||||
}
|
||||
|
||||
constraint_recovery_type :: proc(checker: ^Checker, constraint: types.Type) -> types.Type {
|
||||
switch constraint {
|
||||
case types.UINT: return types.U64
|
||||
case types.FLOAT: return types.F64
|
||||
case types.RANGE: return types.range(&checker.module.types, types.I64)
|
||||
case: return types.I64
|
||||
}
|
||||
}
|
||||
|
||||
type_from_syntax :: proc(
|
||||
checker: ^Checker,
|
||||
value: ast.Type_Syntax,
|
||||
@@ -2647,7 +2667,7 @@ call_arg_expected :: proc(checker: ^Checker, function: ast.Function, index: int)
|
||||
declared := type_from_syntax(checker, function.params[index].type, function.pkg, function.file)
|
||||
// A `float` param defaults to f64 so an integer-literal argument builds as a
|
||||
// float constant (e.g. `f(3)` -> 3.0), mirroring `pi float = 3` for locals.
|
||||
// `int`/`range` constraints have no single default and keep building naturally.
|
||||
// Integer/range constraints keep building naturally from their literals.
|
||||
if declared == types.FLOAT {
|
||||
return types.F64
|
||||
}
|
||||
@@ -3624,13 +3644,21 @@ record_field_expr_candidate :: proc(
|
||||
}
|
||||
expr := checker.ast_module.exprs[expr_id]
|
||||
constraint := checker.record_field_constraints[slot]
|
||||
if constant := eval_integer_constant_in_context(checker, expr_id, pkg, file);
|
||||
constant.kind == .Value && fits_i64(constant.value) {
|
||||
candidate := types.smallest_signed_for_literal(i64(constant.value))
|
||||
if constraint == types.FLOAT {
|
||||
candidate = types.F64
|
||||
if !numeric_operand_is_open(checker, expr_id, locals, pkg, file) {
|
||||
concrete := types.constraint_target(constraint, inferred, &checker.module.types)
|
||||
if is_runtime_type(checker, concrete) {
|
||||
return merge_record_field_demand(checker, slot, concrete, expr.span)
|
||||
}
|
||||
}
|
||||
if constant := eval_integer_constant_in_context(checker, expr_id, pkg, file);
|
||||
constant.kind == .Value {
|
||||
candidate := constraint_integer_literal_type(constraint, constant.value)
|
||||
if constraint == types.FLOAT {
|
||||
candidate = types.F64 if fits_i64(constant.value) else types.INVALID
|
||||
}
|
||||
if types.is_valid(candidate) {
|
||||
return merge_record_field_default(checker, slot, candidate, expr.span)
|
||||
}
|
||||
return merge_record_field_default(checker, slot, candidate, expr.span)
|
||||
}
|
||||
if is_float_constant_expr(checker, expr_id) {
|
||||
return merge_record_field_default(checker, slot, types.F64, expr.span)
|
||||
@@ -3693,14 +3721,7 @@ finalize_record_field_inference :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
if types.is_constraint(current) {
|
||||
switch constraint {
|
||||
case types.INT:
|
||||
checker.module.types.fields[slot].type = types.I64
|
||||
case types.FLOAT:
|
||||
checker.module.types.fields[slot].type = types.F64
|
||||
case types.RANGE:
|
||||
checker.module.types.fields[slot].type = types.range(&checker.module.types, types.I64)
|
||||
}
|
||||
checker.module.types.fields[slot].type = constraint_recovery_type(checker, constraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3999,7 +4020,7 @@ find_spec :: proc(
|
||||
}
|
||||
|
||||
// specialized_param_type maps a parameter's declared type to its monomorphized
|
||||
// type for a given actual argument. A constraint param (`int`/`float`/`range`)
|
||||
// type for a given actual argument. A constraint param (`int`/`uint`/`float`/`range`)
|
||||
// resolves to the actual's family member (INVALID if out of family), so a call
|
||||
// passing an out-of-family argument fails to specialize and is rejected.
|
||||
specialized_param_type :: proc(
|
||||
@@ -4426,14 +4447,16 @@ infer_expr :: proc(
|
||||
if expr.kind != .Name || symbol.is_valid(expr.qualifier) || !static_name {
|
||||
constant = eval_constant(checker, frame.expr)
|
||||
}
|
||||
if constant.kind == .Overflow || constant.kind == .Div_By_Zero ||
|
||||
(constant.kind == .Value && !fits_i64(constant.value)) {
|
||||
if constant.kind == .Overflow || constant.kind == .Div_By_Zero {
|
||||
last = types.I64
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if constant.kind == .Value {
|
||||
last = types.smallest_signed_for_literal(i64(constant.value))
|
||||
last = constraint_integer_literal_type(frame.expected, constant.value)
|
||||
if !types.is_valid(last) {
|
||||
last = types.I64
|
||||
}
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
@@ -5066,7 +5089,7 @@ infer_statements :: proc(
|
||||
declared_local := resolve_inferred_array(checker, type_from_syntax(checker, statement.type, pkg, file), statement.expr)
|
||||
value_type := types.INVALID
|
||||
if !is_undefined_expr(checker, statement.expr) {
|
||||
expected := declared_local if is_runtime_type(checker, declared_local) else types.INVALID
|
||||
expected := declared_local if is_runtime_type(checker, declared_local) || declared_local == types.UINT else types.INVALID
|
||||
value_type = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types, expected)
|
||||
declared_local = resolve_inferred_array_from_type(checker, declared_local, value_type)
|
||||
}
|
||||
@@ -5082,7 +5105,8 @@ infer_statements :: proc(
|
||||
const_val := i128(0)
|
||||
if !is_runtime_type(checker, declared_local) && !is_undefined_expr(checker, statement.expr) {
|
||||
constant := eval_integer_constant_in_context(checker, statement.expr, pkg, file)
|
||||
if constant.kind == .Value && fits_i64(constant.value) {
|
||||
if constant.kind == .Value &&
|
||||
(fits_i64(constant.value) || declared_local == types.UINT && fits_u64(constant.value)) {
|
||||
open = true
|
||||
const_val = constant.value
|
||||
} else if is_float_constant_expr(checker, statement.expr) {
|
||||
@@ -5123,11 +5147,25 @@ infer_statements :: proc(
|
||||
expected_assignment := types.INVALID
|
||||
if statement.target != ast.INVALID_EXPR {
|
||||
expected_assignment = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
|
||||
target_expr := checker.ast_module.exprs[statement.target]
|
||||
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
|
||||
if local_index, ok := find_infer_local_index(locals^[:], target_expr.name); ok &&
|
||||
locals^[local_index].declared == types.UINT {
|
||||
expected_assignment = types.UINT
|
||||
} else if global := find_global(checker, target_expr.name, pkg, file); global != ast.INVALID_GLOBAL {
|
||||
ast_global := checker.ast_module.globals[global]
|
||||
if type_from_syntax(checker, ast_global.type, ast_global.pkg, ast_global.file) == types.UINT {
|
||||
expected_assignment = types.UINT
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if statement.name != checker.sink_symbol {
|
||||
if local_index, ok := find_infer_local_index(locals^[:], statement.name); ok {
|
||||
expected_assignment = locals^[local_index].type
|
||||
expected_assignment = types.UINT if locals^[local_index].declared == types.UINT else locals^[local_index].type
|
||||
} else if global := find_global(checker, statement.name, pkg, file); global != ast.INVALID_GLOBAL {
|
||||
expected_assignment = checker.global_types[global]
|
||||
ast_global := checker.ast_module.globals[global]
|
||||
declared_global := type_from_syntax(checker, ast_global.type, ast_global.pkg, ast_global.file)
|
||||
expected_assignment = types.UINT if declared_global == types.UINT else checker.global_types[global]
|
||||
}
|
||||
}
|
||||
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types, expected_assignment)
|
||||
@@ -5430,7 +5468,7 @@ infer_spec_locals_and_result :: proc(
|
||||
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT {
|
||||
declared = types.I32
|
||||
}
|
||||
result_hint := declared if is_runtime_type(checker, declared) else types.INVALID
|
||||
result_hint := declared if is_runtime_type(checker, declared) || declared == types.UINT else types.INVALID
|
||||
|
||||
locals: [dynamic]Infer_Local
|
||||
locals.allocator = checker.allocator
|
||||
@@ -5615,7 +5653,7 @@ open_const_default_type :: proc(
|
||||
if index, ok := find_infer_local_index(locals, expr.name); ok {
|
||||
local := locals[index]
|
||||
if local.open_const {
|
||||
return types.smallest_signed_for_literal(i64(local.const_value))
|
||||
return constraint_integer_literal_type(local.declared, local.const_value)
|
||||
}
|
||||
if local.open_float {
|
||||
return types.F64
|
||||
@@ -5633,7 +5671,9 @@ open_const_default_type :: proc(
|
||||
return types.INVALID
|
||||
}
|
||||
if checker.global_open_const[index] {
|
||||
return types.smallest_signed_for_literal(i64(checker.global_const_value[index]))
|
||||
ast_global := checker.ast_module.globals[global]
|
||||
declared := type_from_syntax(checker, ast_global.type, ast_global.pkg, ast_global.file)
|
||||
return constraint_integer_literal_type(declared, checker.global_const_value[index])
|
||||
}
|
||||
if checker.global_open_float[index] {
|
||||
return types.F64
|
||||
@@ -5899,7 +5939,8 @@ infer_all :: proc(checker: ^Checker) {
|
||||
}
|
||||
}
|
||||
constant := eval_integer_constant_in_context(checker, global.expr, global.pkg, global.file)
|
||||
if constant.kind == .Value && fits_i64(constant.value) {
|
||||
if constant.kind == .Value &&
|
||||
(fits_i64(constant.value) || declared == types.UINT && fits_u64(constant.value)) {
|
||||
checker.global_open_const[index] = true
|
||||
checker.global_const_value[index] = constant.value
|
||||
} else if is_float_constant_expr(checker, global.expr) {
|
||||
@@ -5947,7 +5988,7 @@ infer_all :: proc(checker: ^Checker) {
|
||||
type_from_syntax(checker, global.type, global.pkg, global.file),
|
||||
global.expr,
|
||||
)
|
||||
expected := declared if is_runtime_type(checker, declared) else types.INVALID
|
||||
expected := declared if is_runtime_type(checker, declared) || declared == types.UINT else types.INVALID
|
||||
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file, expected=expected)
|
||||
if resolved := resolve_inferred_array_from_type(checker, declared, inferred);
|
||||
resolved != declared {
|
||||
@@ -6020,7 +6061,8 @@ infer_all :: proc(checker: ^Checker) {
|
||||
continue
|
||||
}
|
||||
if checker.global_open_const[index] {
|
||||
checker.global_types[index] = types.smallest_signed_for_literal(i64(checker.global_const_value[index]))
|
||||
declared := type_from_syntax(checker, global.type, global.pkg, global.file)
|
||||
checker.global_types[index] = constraint_integer_literal_type(declared, checker.global_const_value[index])
|
||||
defaulted = true
|
||||
} else if checker.global_open_float[index] {
|
||||
checker.global_types[index] = types.F64
|
||||
@@ -6394,7 +6436,7 @@ build_constant_expr :: proc(
|
||||
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
||||
}
|
||||
if constant.kind == .Overflow ||
|
||||
(!types.is_concrete_integer(expected) && !fits_i64(constant.value)) {
|
||||
(!types.is_concrete_integer(expected) && expected != types.UINT && !fits_i64(constant.value)) {
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
@@ -6407,7 +6449,16 @@ build_constant_expr :: proc(
|
||||
if constant.value >= 0 && constant.value <= i128(0xffff_ffff_ffff_ffff) {
|
||||
value = transmute(i64)u64(constant.value)
|
||||
}
|
||||
result_type := types.smallest_signed_for_literal(value)
|
||||
result_type := constraint_integer_literal_type(expected, constant.value)
|
||||
if expected == types.UINT && !types.is_valid(result_type) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
"integer constant %d does not satisfy uint",
|
||||
constant.value,
|
||||
)
|
||||
return invalid_hir_expr(checker, expr.span, id, types.U64)
|
||||
}
|
||||
if types.is_concrete_integer(expected) {
|
||||
if !fits_integer_type(constant.value, expected, checker.target) {
|
||||
id := source.addf(
|
||||
@@ -12375,11 +12426,12 @@ build_globals :: proc(checker: ^Checker) {
|
||||
if is_runtime_type(checker, declared) {
|
||||
expected = declared
|
||||
} else if constant := eval_integer_constant_in_context(checker, global.expr, global.pkg, global.file);
|
||||
constant.kind == .Value && fits_i64(constant.value) &&
|
||||
constant.kind == .Value &&
|
||||
(fits_i64(constant.value) || declared == types.UINT && fits_u64(constant.value)) &&
|
||||
is_runtime_type(checker, checker.global_types[global_index]) {
|
||||
// Open integer constant: build against its demanded/defaulted type. Gated
|
||||
// to the infer-side open-constant condition so out-of-range constants keep
|
||||
// their original "exceeds signed i64 range" diagnostic.
|
||||
// their original range diagnostic.
|
||||
expected = checker.global_types[global_index]
|
||||
} else if (is_float_constant_expr(checker, global.expr) ||
|
||||
is_numeric_arithmetic_expr(checker, global.expr)) &&
|
||||
@@ -12401,6 +12453,18 @@ build_globals :: proc(checker: ^Checker) {
|
||||
global_type = types.I64
|
||||
}
|
||||
}
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC && types.is_constraint(declared) &&
|
||||
!types.constraint_accepts(declared, global_type, &checker.module.types) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"could not resolve the '%s' constraint for global '%s'",
|
||||
types.name(declared),
|
||||
symbol_text(checker, global.name),
|
||||
)
|
||||
global_type = constraint_recovery_type(checker, declared)
|
||||
expr = invalid_hir_expr(checker, global.span, diagnostic, global_type)
|
||||
}
|
||||
if !global.immutable && is_undefined_expr(checker, global.expr) {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
|
||||
Reference in New Issue
Block a user