unsigned integer constraint (uint)
This commit is contained in:
+5
-4
@@ -24,7 +24,7 @@ roadmap and milestone history.
|
|||||||
|
|
||||||
### scalar, aggregate, and pointer types
|
### scalar, aggregate, and pointer types
|
||||||
|
|
||||||
- exact-width integers, `isize`, `usize`, `f32`, `f64`, `bool`, `void`, `anyopaque`, and contextual `int`, `float`, and `range` constraints
|
- exact-width integers, concrete pointer-sized `isize` / `usize`, `f32`, `f64`, `bool`, `void`, and `anyopaque`; contextual `int` accepts the whole integer family, while `uint` accepts only unsigned native and target-classified C integers
|
||||||
- target-dependent C scalar primitives from `c_char` through `c_longdouble`, kept semantically distinct from native scalars
|
- target-dependent C scalar primitives from `c_char` through `c_longdouble`, kept semantically distinct from native scalars
|
||||||
- contextual integer/float/character literals, backward type-demand inference through names and arithmetic, and compile-time folding for numeric constant expressions
|
- contextual integer/float/character literals, backward type-demand inference through names and arithmetic, and compile-time folding for numeric constant expressions
|
||||||
- strict numeric conversion by default, widening where valid, C scalar coercions at C boundaries, and explicit scalar keyword casts such as `i32(x)` / `c_float(x)`
|
- strict numeric conversion by default, widening where valid, C scalar coercions at C boundaries, and explicit scalar keyword casts such as `i32(x)` / `c_float(x)`
|
||||||
@@ -44,16 +44,17 @@ roadmap and milestone history.
|
|||||||
|
|
||||||
#### native record constraint fields
|
#### native record constraint fields
|
||||||
|
|
||||||
A direct `int`, `float`, or `range` field in a named native struct or union is a
|
A direct `int`, `uint`, `float`, or `range` field in a named native struct or union is a
|
||||||
program-wide constraint, not per-value polymorphism. Before record layout, all reachable keyed
|
program-wide constraint, not per-value polymorphism. Before record layout, all reachable keyed
|
||||||
constructors, field assignments, and concrete uses of field reads contribute demands and the field
|
constructors, field assignments, and concrete uses of field reads contribute demands and the field
|
||||||
resolves once to one concrete runtime type. Compatible scalar demands widen normally. Integer
|
resolves once to one concrete runtime type. Compatible scalar demands widen normally. Integer
|
||||||
literals remain provisional until inference settles, so a later `usize` use can resolve an `int`
|
literals remain provisional until inference settles, so a later `usize` use can resolve an `int`
|
||||||
field to `usize`; otherwise literal-only `int` fields use the widest smallest-signed type required,
|
field to `usize`; otherwise literal-only `int` fields use the widest smallest-signed type required,
|
||||||
and literal-only `float` fields use `f64`.
|
literal-only `uint` fields use the widest smallest-unsigned type required, and literal-only `float`
|
||||||
|
fields use `f64`.
|
||||||
|
|
||||||
An undemanded field or incompatible demands are errors. This inference applies only to direct
|
An undemanded field or incompatible demands are errors. This inference applies only to direct
|
||||||
fields of named native records. `c_struct` fields, nested constraints such as `[]int`, and fields in
|
fields of named native records. `c_struct` fields, nested constraints such as `[]int` / `[]uint`, and fields in
|
||||||
anonymous generated records still require concrete runtime types.
|
anonymous generated records still require concrete runtime types.
|
||||||
|
|
||||||
#### keyword member names
|
#### keyword member names
|
||||||
|
|||||||
@@ -907,6 +907,12 @@
|
|||||||
- filtering, skipping, fixtures, snapshots, parallelism, isolation, allocators, and more assertion
|
- filtering, skipping, fixtures, snapshots, parallelism, isolation, allocators, and more assertion
|
||||||
families remain deferred
|
families remain deferred
|
||||||
|
|
||||||
|
42. unsigned integer constraint (implemented)
|
||||||
|
- `uint` is the unsigned subset constraint while `int` remains the whole integer family
|
||||||
|
- literal-only values choose the smallest fitting `u8`, `u16`, `u32`, or `u64`
|
||||||
|
- native unsigned scalars and target-classified unsigned C scalars satisfy the constraint
|
||||||
|
- `isize` and `usize` remain concrete pointer-sized types
|
||||||
|
|
||||||
## A word on unchecked casts
|
## A word on unchecked casts
|
||||||
|
|
||||||
For casts that bypass safety checks, Honey provides builtin functions:
|
For casts that bypass safety checks, Honey provides builtin functions:
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ write_type_label :: proc(checker: ^Checker, builder: ^strings.Builder, value: ty
|
|||||||
strings.write_string(builder, "enum")
|
strings.write_string(builder, "enum")
|
||||||
case .Alias, .Distinct, .Named:
|
case .Alias, .Distinct, .Named:
|
||||||
write_type_label(checker, builder, item.child)
|
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))
|
strings.write_string(builder, types.name(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1052,6 +1052,26 @@ fits_i64 :: proc(value: i128) -> bool {
|
|||||||
return fits_signed_type(value, types.I64)
|
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(
|
type_from_syntax :: proc(
|
||||||
checker: ^Checker,
|
checker: ^Checker,
|
||||||
value: ast.Type_Syntax,
|
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)
|
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
|
// 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.
|
// 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 {
|
if declared == types.FLOAT {
|
||||||
return types.F64
|
return types.F64
|
||||||
}
|
}
|
||||||
@@ -3624,13 +3644,21 @@ record_field_expr_candidate :: proc(
|
|||||||
}
|
}
|
||||||
expr := checker.ast_module.exprs[expr_id]
|
expr := checker.ast_module.exprs[expr_id]
|
||||||
constraint := checker.record_field_constraints[slot]
|
constraint := checker.record_field_constraints[slot]
|
||||||
if constant := eval_integer_constant_in_context(checker, expr_id, pkg, file);
|
if !numeric_operand_is_open(checker, expr_id, locals, pkg, file) {
|
||||||
constant.kind == .Value && fits_i64(constant.value) {
|
concrete := types.constraint_target(constraint, inferred, &checker.module.types)
|
||||||
candidate := types.smallest_signed_for_literal(i64(constant.value))
|
if is_runtime_type(checker, concrete) {
|
||||||
if constraint == types.FLOAT {
|
return merge_record_field_demand(checker, slot, concrete, expr.span)
|
||||||
candidate = types.F64
|
}
|
||||||
|
}
|
||||||
|
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) {
|
if is_float_constant_expr(checker, expr_id) {
|
||||||
return merge_record_field_default(checker, slot, types.F64, expr.span)
|
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) {
|
if types.is_constraint(current) {
|
||||||
switch constraint {
|
checker.module.types.fields[slot].type = constraint_recovery_type(checker, 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3999,7 +4020,7 @@ find_spec :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// specialized_param_type maps a parameter's declared type to its monomorphized
|
// 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
|
// 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.
|
// passing an out-of-family argument fails to specialize and is rejected.
|
||||||
specialized_param_type :: proc(
|
specialized_param_type :: proc(
|
||||||
@@ -4426,14 +4447,16 @@ infer_expr :: proc(
|
|||||||
if expr.kind != .Name || symbol.is_valid(expr.qualifier) || !static_name {
|
if expr.kind != .Name || symbol.is_valid(expr.qualifier) || !static_name {
|
||||||
constant = eval_constant(checker, frame.expr)
|
constant = eval_constant(checker, frame.expr)
|
||||||
}
|
}
|
||||||
if constant.kind == .Overflow || constant.kind == .Div_By_Zero ||
|
if constant.kind == .Overflow || constant.kind == .Div_By_Zero {
|
||||||
(constant.kind == .Value && !fits_i64(constant.value)) {
|
|
||||||
last = types.I64
|
last = types.I64
|
||||||
_ = pop(&stack)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if constant.kind == .Value {
|
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)
|
_ = pop(&stack)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -5066,7 +5089,7 @@ infer_statements :: proc(
|
|||||||
declared_local := resolve_inferred_array(checker, type_from_syntax(checker, statement.type, pkg, file), statement.expr)
|
declared_local := resolve_inferred_array(checker, type_from_syntax(checker, statement.type, pkg, file), statement.expr)
|
||||||
value_type := types.INVALID
|
value_type := types.INVALID
|
||||||
if !is_undefined_expr(checker, statement.expr) {
|
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)
|
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)
|
declared_local = resolve_inferred_array_from_type(checker, declared_local, value_type)
|
||||||
}
|
}
|
||||||
@@ -5082,7 +5105,8 @@ infer_statements :: proc(
|
|||||||
const_val := i128(0)
|
const_val := i128(0)
|
||||||
if !is_runtime_type(checker, declared_local) && !is_undefined_expr(checker, statement.expr) {
|
if !is_runtime_type(checker, declared_local) && !is_undefined_expr(checker, statement.expr) {
|
||||||
constant := eval_integer_constant_in_context(checker, statement.expr, pkg, file)
|
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
|
open = true
|
||||||
const_val = constant.value
|
const_val = constant.value
|
||||||
} else if is_float_constant_expr(checker, statement.expr) {
|
} else if is_float_constant_expr(checker, statement.expr) {
|
||||||
@@ -5123,11 +5147,25 @@ infer_statements :: proc(
|
|||||||
expected_assignment := types.INVALID
|
expected_assignment := types.INVALID
|
||||||
if statement.target != ast.INVALID_EXPR {
|
if statement.target != ast.INVALID_EXPR {
|
||||||
expected_assignment = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
|
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 {
|
} else if statement.name != checker.sink_symbol {
|
||||||
if local_index, ok := find_infer_local_index(locals^[:], statement.name); ok {
|
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 {
|
} 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)
|
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 {
|
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT {
|
||||||
declared = types.I32
|
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: [dynamic]Infer_Local
|
||||||
locals.allocator = checker.allocator
|
locals.allocator = checker.allocator
|
||||||
@@ -5615,7 +5653,7 @@ open_const_default_type :: proc(
|
|||||||
if index, ok := find_infer_local_index(locals, expr.name); ok {
|
if index, ok := find_infer_local_index(locals, expr.name); ok {
|
||||||
local := locals[index]
|
local := locals[index]
|
||||||
if local.open_const {
|
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 {
|
if local.open_float {
|
||||||
return types.F64
|
return types.F64
|
||||||
@@ -5633,7 +5671,9 @@ open_const_default_type :: proc(
|
|||||||
return types.INVALID
|
return types.INVALID
|
||||||
}
|
}
|
||||||
if checker.global_open_const[index] {
|
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] {
|
if checker.global_open_float[index] {
|
||||||
return types.F64
|
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)
|
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_open_const[index] = true
|
||||||
checker.global_const_value[index] = constant.value
|
checker.global_const_value[index] = constant.value
|
||||||
} else if is_float_constant_expr(checker, global.expr) {
|
} 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),
|
type_from_syntax(checker, global.type, global.pkg, global.file),
|
||||||
global.expr,
|
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)
|
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file, expected=expected)
|
||||||
if resolved := resolve_inferred_array_from_type(checker, declared, inferred);
|
if resolved := resolve_inferred_array_from_type(checker, declared, inferred);
|
||||||
resolved != declared {
|
resolved != declared {
|
||||||
@@ -6020,7 +6061,8 @@ infer_all :: proc(checker: ^Checker) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if checker.global_open_const[index] {
|
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
|
defaulted = true
|
||||||
} else if checker.global_open_float[index] {
|
} else if checker.global_open_float[index] {
|
||||||
checker.global_types[index] = types.F64
|
checker.global_types[index] = types.F64
|
||||||
@@ -6394,7 +6436,7 @@ build_constant_expr :: proc(
|
|||||||
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
return invalid_hir_expr(checker, expr.span, id, recovery_type)
|
||||||
}
|
}
|
||||||
if constant.kind == .Overflow ||
|
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(
|
id := source.add(
|
||||||
checker.diagnostics,
|
checker.diagnostics,
|
||||||
expr.span,
|
expr.span,
|
||||||
@@ -6407,7 +6449,16 @@ build_constant_expr :: proc(
|
|||||||
if constant.value >= 0 && constant.value <= i128(0xffff_ffff_ffff_ffff) {
|
if constant.value >= 0 && constant.value <= i128(0xffff_ffff_ffff_ffff) {
|
||||||
value = transmute(i64)u64(constant.value)
|
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 types.is_concrete_integer(expected) {
|
||||||
if !fits_integer_type(constant.value, expected, checker.target) {
|
if !fits_integer_type(constant.value, expected, checker.target) {
|
||||||
id := source.addf(
|
id := source.addf(
|
||||||
@@ -12375,11 +12426,12 @@ build_globals :: proc(checker: ^Checker) {
|
|||||||
if is_runtime_type(checker, declared) {
|
if is_runtime_type(checker, declared) {
|
||||||
expected = declared
|
expected = declared
|
||||||
} else if constant := eval_integer_constant_in_context(checker, global.expr, global.pkg, global.file);
|
} 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]) {
|
is_runtime_type(checker, checker.global_types[global_index]) {
|
||||||
// Open integer constant: build against its demanded/defaulted type. Gated
|
// Open integer constant: build against its demanded/defaulted type. Gated
|
||||||
// to the infer-side open-constant condition so out-of-range constants keep
|
// 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]
|
expected = checker.global_types[global_index]
|
||||||
} else if (is_float_constant_expr(checker, global.expr) ||
|
} else if (is_float_constant_expr(checker, global.expr) ||
|
||||||
is_numeric_arithmetic_expr(checker, global.expr)) &&
|
is_numeric_arithmetic_expr(checker, global.expr)) &&
|
||||||
@@ -12401,6 +12453,18 @@ build_globals :: proc(checker: ^Checker) {
|
|||||||
global_type = types.I64
|
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) {
|
if !global.immutable && is_undefined_expr(checker, global.expr) {
|
||||||
diagnostic = source.add(
|
diagnostic = source.add(
|
||||||
checker.diagnostics,
|
checker.diagnostics,
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
|||||||
case "anyopaque": return .Keyword_Anyopaque
|
case "anyopaque": return .Keyword_Anyopaque
|
||||||
case "bool": return .Keyword_Bool
|
case "bool": return .Keyword_Bool
|
||||||
case "int": return .Keyword_Int
|
case "int": return .Keyword_Int
|
||||||
|
case "uint": return .Keyword_Uint
|
||||||
case "float": return .Keyword_Float
|
case "float": return .Keyword_Float
|
||||||
case "range": return .Keyword_Range
|
case "range": return .Keyword_Range
|
||||||
case "i8": return .Keyword_I8
|
case "i8": return .Keyword_I8
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ invalid_expr :: proc(parser: ^Parser, span: source.Span, message: string) -> ast
|
|||||||
|
|
||||||
is_type_token :: proc(kind: token.Kind) -> bool {
|
is_type_token :: proc(kind: token.Kind) -> bool {
|
||||||
#partial switch kind {
|
#partial switch kind {
|
||||||
case .Keyword_Int, .Keyword_Float, .Keyword_Range, .Keyword_I8, .Keyword_I16, .Keyword_I32, .Keyword_I64,
|
case .Keyword_Int, .Keyword_Uint, .Keyword_Float, .Keyword_Range, .Keyword_I8, .Keyword_I16, .Keyword_I32, .Keyword_I64,
|
||||||
.Keyword_U8, .Keyword_U16, .Keyword_U32, .Keyword_U64,
|
.Keyword_U8, .Keyword_U16, .Keyword_U32, .Keyword_U64,
|
||||||
.Keyword_Isize, .Keyword_Usize, .Keyword_F32, .Keyword_F64,
|
.Keyword_Isize, .Keyword_Usize, .Keyword_F32, .Keyword_F64,
|
||||||
.Keyword_C_Char, .Keyword_C_Schar, .Keyword_C_Uchar,
|
.Keyword_C_Char, .Keyword_C_Schar, .Keyword_C_Uchar,
|
||||||
@@ -280,6 +280,9 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
|||||||
case .Keyword_Int:
|
case .Keyword_Int:
|
||||||
advance(parser)
|
advance(parser)
|
||||||
return types.INT
|
return types.INT
|
||||||
|
case .Keyword_Uint:
|
||||||
|
advance(parser)
|
||||||
|
return types.UINT
|
||||||
case .Keyword_Float:
|
case .Keyword_Float:
|
||||||
advance(parser)
|
advance(parser)
|
||||||
return types.FLOAT
|
return types.FLOAT
|
||||||
@@ -774,7 +777,7 @@ parse_integer_magnitude :: proc(text: string) -> (u64, bool) {
|
|||||||
parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||||
tok := current(parser)
|
tok := current(parser)
|
||||||
#partial switch tok.kind {
|
#partial switch tok.kind {
|
||||||
case .Keyword_Int, .Keyword_Float, .Keyword_Range, .Keyword_Void, .Keyword_Anyopaque, .Keyword_Bool:
|
case .Keyword_Int, .Keyword_Uint, .Keyword_Float, .Keyword_Range, .Keyword_Void, .Keyword_Anyopaque, .Keyword_Bool:
|
||||||
start := tok
|
start := tok
|
||||||
target := parse_type_atom(parser)
|
target := parse_type_atom(parser)
|
||||||
return add_expr(parser, ast.Expr{
|
return add_expr(parser, ast.Expr{
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ Kind :: enum u8 {
|
|||||||
Keyword_Anyopaque,
|
Keyword_Anyopaque,
|
||||||
Keyword_Bool,
|
Keyword_Bool,
|
||||||
Keyword_Int,
|
Keyword_Int,
|
||||||
|
Keyword_Uint,
|
||||||
Keyword_Float,
|
Keyword_Float,
|
||||||
Keyword_Range,
|
Keyword_Range,
|
||||||
Keyword_I8,
|
Keyword_I8,
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ BOOL :: Type(29)
|
|||||||
FLOAT :: Type(30)
|
FLOAT :: Type(30)
|
||||||
RANGE :: Type(31)
|
RANGE :: Type(31)
|
||||||
ANYOPAQUE :: Type(32)
|
ANYOPAQUE :: Type(32)
|
||||||
|
UINT :: Type(33)
|
||||||
|
|
||||||
DYNAMIC_START :: Type(64)
|
DYNAMIC_START :: Type(64)
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ Kind :: enum u8 {
|
|||||||
Void,
|
Void,
|
||||||
Anyopaque,
|
Anyopaque,
|
||||||
Int_Constraint,
|
Int_Constraint,
|
||||||
|
Uint_Constraint,
|
||||||
Float_Constraint,
|
Float_Constraint,
|
||||||
Range_Constraint,
|
Range_Constraint,
|
||||||
Scalar,
|
Scalar,
|
||||||
@@ -595,6 +597,8 @@ kind :: proc(value: Type, store: ^Store = nil) -> Kind {
|
|||||||
return .Anyopaque
|
return .Anyopaque
|
||||||
case INT:
|
case INT:
|
||||||
return .Int_Constraint
|
return .Int_Constraint
|
||||||
|
case UINT:
|
||||||
|
return .Uint_Constraint
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
return .Float_Constraint
|
return .Float_Constraint
|
||||||
case RANGE:
|
case RANGE:
|
||||||
@@ -642,7 +646,7 @@ is_bool :: proc(value: Type) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is_constraint :: proc(value: Type) -> bool {
|
is_constraint :: proc(value: Type) -> bool {
|
||||||
return value == INT || value == FLOAT || value == RANGE
|
return value == INT || value == UINT || value == FLOAT || value == RANGE
|
||||||
}
|
}
|
||||||
|
|
||||||
// constraint_target reports the concrete type a constraint binding (local, or a
|
// constraint_target reports the concrete type a constraint binding (local, or a
|
||||||
@@ -656,6 +660,8 @@ constraint_target :: proc(constraint, inferred: Type, store: ^Store = nil) -> Ty
|
|||||||
switch constraint {
|
switch constraint {
|
||||||
case INT:
|
case INT:
|
||||||
return inferred if is_concrete_integer(inferred) else INVALID
|
return inferred if is_concrete_integer(inferred) else INVALID
|
||||||
|
case UINT:
|
||||||
|
return inferred if is_unsigned(inferred) else INVALID
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
if is_float(inferred) {
|
if is_float(inferred) {
|
||||||
return inferred
|
return inferred
|
||||||
@@ -673,6 +679,8 @@ constraint_accepts :: proc(constraint, concrete: Type, store: ^Store = nil) -> b
|
|||||||
switch constraint {
|
switch constraint {
|
||||||
case INT:
|
case INT:
|
||||||
return is_concrete_integer(concrete)
|
return is_concrete_integer(concrete)
|
||||||
|
case UINT:
|
||||||
|
return is_unsigned(concrete)
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
return is_float(concrete)
|
return is_float(concrete)
|
||||||
case RANGE:
|
case RANGE:
|
||||||
@@ -1686,6 +1694,19 @@ smallest_signed_for_literal :: proc(value: i64) -> Type {
|
|||||||
return I64
|
return I64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smallest_unsigned_for_literal :: proc(value: u64) -> Type {
|
||||||
|
if value <= 255 {
|
||||||
|
return U8
|
||||||
|
}
|
||||||
|
if value <= 65535 {
|
||||||
|
return U16
|
||||||
|
}
|
||||||
|
if value <= 4294967295 {
|
||||||
|
return U32
|
||||||
|
}
|
||||||
|
return U64
|
||||||
|
}
|
||||||
|
|
||||||
name :: proc(value: Type) -> string {
|
name :: proc(value: Type) -> string {
|
||||||
switch value {
|
switch value {
|
||||||
case INVALID: return "<invalid>"
|
case INVALID: return "<invalid>"
|
||||||
@@ -1693,6 +1714,7 @@ name :: proc(value: Type) -> string {
|
|||||||
case ANYOPAQUE: return "anyopaque"
|
case ANYOPAQUE: return "anyopaque"
|
||||||
case BOOL: return "bool"
|
case BOOL: return "bool"
|
||||||
case INT: return "int"
|
case INT: return "int"
|
||||||
|
case UINT: return "uint"
|
||||||
case FLOAT: return "float"
|
case FLOAT: return "float"
|
||||||
case RANGE: return "range"
|
case RANGE: return "range"
|
||||||
case I8: return "i8"
|
case I8: return "i8"
|
||||||
|
|||||||
+188
-1
@@ -1485,6 +1485,7 @@ main func() void {
|
|||||||
_ = maxval!(u8, u16)
|
_ = maxval!(u8, u16)
|
||||||
_ = minval!(1)
|
_ = minval!(1)
|
||||||
_ = maxval!(int)
|
_ = maxval!(int)
|
||||||
|
_ = maxval!(uint)
|
||||||
_ = maxval!(f32)
|
_ = maxval!(f32)
|
||||||
_ = maxval!(bool)
|
_ = maxval!(bool)
|
||||||
_ = maxval!(Named)
|
_ = maxval!(Named)
|
||||||
@@ -1513,7 +1514,7 @@ main func() void {
|
|||||||
}
|
}
|
||||||
testing.expect_value(t, bad_arity, 2)
|
testing.expect_value(t, bad_arity, 2)
|
||||||
testing.expect(t, bad_type)
|
testing.expect(t, bad_type)
|
||||||
testing.expect_value(t, bad_target, 5)
|
testing.expect_value(t, bad_target, 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
@@ -12876,3 +12877,189 @@ dependency_passes test {
|
|||||||
testing.expect(t, strings.contains(output, root_path))
|
testing.expect(t, strings.contains(output, root_path))
|
||||||
testing.expect(t, strings.contains(output, "3 passed, 1 failed"))
|
testing.expect(t, strings.contains(output, "3 passed, 1 failed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
uint_constraint_parses_as_type_and_comptime_value :: proc(t: ^testing.T) {
|
||||||
|
text := `value uint :: 1
|
||||||
|
Constraint :: uint
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="uint.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&module)
|
||||||
|
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
testing.expect_value(t, len(module.globals), 2)
|
||||||
|
testing.expect_value(t, module.globals[0].type, types.UINT)
|
||||||
|
testing.expect_value(t, module.exprs[module.globals[1].expr].kind, ast.Expr_Kind.Type)
|
||||||
|
testing.expect_value(t, module.exprs[module.globals[1].expr].type, types.UINT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
uint_constraint_uses_smallest_unsigned_types_and_widens :: proc(t: ^testing.T) {
|
||||||
|
text := `Zero uint :: 0
|
||||||
|
Byte uint :: 255
|
||||||
|
Word uint :: 256
|
||||||
|
Dword uint :: 65536
|
||||||
|
Maximum uint :: 18446744073709551615
|
||||||
|
Counter uint = 1
|
||||||
|
|
||||||
|
widen func() uint {
|
||||||
|
value uint = 1
|
||||||
|
value = 1000
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
widen_global func() void { Counter = 1000 }
|
||||||
|
|
||||||
|
main func() void {
|
||||||
|
_ = Zero
|
||||||
|
_ = Byte
|
||||||
|
_ = Word
|
||||||
|
_ = Dword
|
||||||
|
_ = Maximum
|
||||||
|
_ = widen()
|
||||||
|
widen_global()
|
||||||
|
}
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="uint_types.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
expected := []types.Type{types.U8, types.U8, types.U16, types.U32, types.U64, types.U16}
|
||||||
|
for value_type, index in expected {
|
||||||
|
testing.expect_value(t, hir_module.globals[index].type, value_type)
|
||||||
|
}
|
||||||
|
widen_symbol := symbol.intern(&symbols, "widen")
|
||||||
|
found_widen := false
|
||||||
|
for function in hir_module.functions {
|
||||||
|
if function.name != widen_symbol {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found_widen = true
|
||||||
|
testing.expect_value(t, function.result, types.U16)
|
||||||
|
testing.expect_value(t, function.locals[0].type, types.U16)
|
||||||
|
}
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
testing.expect(t, found_widen)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
uint_constraint_infers_boundaries_calls_and_records :: proc(t: ^testing.T) {
|
||||||
|
directory := "/tmp/brolang-test-uint-constraint"
|
||||||
|
main_path := "/tmp/brolang-test-uint-constraint/main.bro"
|
||||||
|
output := "/tmp/brolang-test-uint-constraint-output"
|
||||||
|
text := `Box :: struct { value uint }
|
||||||
|
|
||||||
|
identity func(value uint) uint { return value }
|
||||||
|
whole func(value int) int { return value }
|
||||||
|
max_value func() uint { return 18446744073709551615 }
|
||||||
|
|
||||||
|
make_box func(value usize) Box { return Box{value = value} }
|
||||||
|
|
||||||
|
main func() i32 {
|
||||||
|
zero uint :: 0
|
||||||
|
byte uint :: 255
|
||||||
|
word uint :: 256
|
||||||
|
word_max uint :: 65535
|
||||||
|
dword uint :: 65536
|
||||||
|
maximum uint :: 18446744073709551615
|
||||||
|
platform usize = 7
|
||||||
|
c_value c_uint = c_uint(9)
|
||||||
|
box Box = make_box(7)
|
||||||
|
signed isize = -1
|
||||||
|
if (identity(zero) != 0) return 1
|
||||||
|
if (identity(byte) != 255) return 2
|
||||||
|
if (identity(word) != 256) return 3
|
||||||
|
if (identity(word_max) != 65535) return 4
|
||||||
|
if (identity(dword) != 65536) return 5
|
||||||
|
if (identity(maximum) != 18446744073709551615) return 6
|
||||||
|
if (box.value != 7) return 7
|
||||||
|
if (signed != -1) return 8
|
||||||
|
if (identity(platform) != 7) return 9
|
||||||
|
if (identity(c_value) != c_value) return 10
|
||||||
|
if (whole(word) != 256) return 11
|
||||||
|
if (max_value() != 18446744073709551615) return 12
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
defer _ = os.remove(output)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||||
|
state := run_executable(output)
|
||||||
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
uint_constraint_rejects_other_families_and_recovers_records_as_u64 :: proc(t: ^testing.T) {
|
||||||
|
text := `Unresolved :: struct { value uint }
|
||||||
|
Conflict :: struct { value uint }
|
||||||
|
|
||||||
|
take func(value uint) uint { return value }
|
||||||
|
bad_result func() uint { return -1 }
|
||||||
|
|
||||||
|
bad func(signed i32, decimal f64) void {
|
||||||
|
_ = take(signed)
|
||||||
|
_ = take(decimal)
|
||||||
|
negative uint :: -1
|
||||||
|
a Conflict = Conflict{value = signed}
|
||||||
|
_ = negative
|
||||||
|
_ = a
|
||||||
|
}
|
||||||
|
|
||||||
|
Overflow uint :: 18446744073709551616
|
||||||
|
NegativeGlobal uint :: -1
|
||||||
|
main func() void {
|
||||||
|
bad(1, 1.0)
|
||||||
|
_ = bad_result()
|
||||||
|
}
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="bad_uint.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
signed_error := false
|
||||||
|
float_error := false
|
||||||
|
negative_error := false
|
||||||
|
field_error := false
|
||||||
|
overflow_error := false
|
||||||
|
global_error := false
|
||||||
|
result_error := false
|
||||||
|
for diagnostic in diagnostics.items {
|
||||||
|
signed_error = signed_error || strings.contains(diagnostic.message, "cannot pass i32 to 'uint' parameter 'value'")
|
||||||
|
float_error = float_error || strings.contains(diagnostic.message, "cannot pass f64 to 'uint' parameter 'value'")
|
||||||
|
negative_error = negative_error || strings.contains(diagnostic.message, "could not resolve the 'uint' constraint for local 'negative'")
|
||||||
|
field_error = field_error || strings.contains(diagnostic.message, "type i32 does not satisfy the 'uint' constraint for field 'Conflict.value'")
|
||||||
|
overflow_error = overflow_error || strings.contains(diagnostic.message, "magnitude does not fit in u64")
|
||||||
|
global_error = global_error || strings.contains(diagnostic.message, "could not resolve the 'uint' constraint for global 'NegativeGlobal'")
|
||||||
|
result_error = result_error || strings.contains(diagnostic.message, "could not resolve a concrete result type for 'bad_result'")
|
||||||
|
}
|
||||||
|
unresolved, unresolved_ok := named_record_field_type(&hir_module, &symbols, "Unresolved", "value")
|
||||||
|
testing.expect(t, signed_error && float_error && negative_error && field_error && overflow_error && global_error && result_error)
|
||||||
|
testing.expect(t, unresolved_ok)
|
||||||
|
testing.expect_value(t, unresolved, types.U64)
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@ languages = ["languages/brolang"]
|
|||||||
|
|
||||||
[grammars.brolang]
|
[grammars.brolang]
|
||||||
repository = "file:///Users/valdemar/Developer/Personal/Languages/brolang"
|
repository = "file:///Users/valdemar/Developer/Personal/Languages/brolang"
|
||||||
rev = "2e8234c6a9a12326f230ea16e901ac41a1b48c99"
|
rev = "71357b2866d2a558f80b6d5cc65761fb853e74d3"
|
||||||
path = "tree-sitter-brolang"
|
path = "tree-sitter-brolang"
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ module.exports = grammar({
|
|||||||
_type_constant: $ => seq(optional('-'), choice($.integer, $.character)),
|
_type_constant: $ => seq(optional('-'), choice($.integer, $.character)),
|
||||||
|
|
||||||
builtin_type: _ => choice(
|
builtin_type: _ => choice(
|
||||||
'void', 'anyopaque', 'bool', 'int', 'float', 'range',
|
'void', 'anyopaque', 'bool', 'int', 'uint', 'float', 'range',
|
||||||
'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64',
|
'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64',
|
||||||
'isize', 'usize', 'f32', 'f64',
|
'isize', 'usize', 'f32', 'f64',
|
||||||
'c_char', 'c_schar', 'c_uchar', 'c_short', 'c_ushort',
|
'c_char', 'c_schar', 'c_uchar', 'c_short', 'c_ushort',
|
||||||
|
|||||||
@@ -1476,6 +1476,10 @@
|
|||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"value": "int"
|
"value": "int"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "STRING",
|
||||||
|
"value": "uint"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"value": "float"
|
"value": "float"
|
||||||
|
|||||||
@@ -2815,6 +2815,10 @@
|
|||||||
"type": "u8",
|
"type": "u8",
|
||||||
"named": false
|
"named": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "uint",
|
||||||
|
"named": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "undefined",
|
"type": "undefined",
|
||||||
"named": true
|
"named": true
|
||||||
|
|||||||
+4262
-2805
File diff suppressed because it is too large
Load Diff
@@ -493,3 +493,19 @@ visit func(kind Kind, value Value) void {
|
|||||||
(sink))
|
(sink))
|
||||||
(expression
|
(expression
|
||||||
(identifier))))))))))))
|
(identifier))))))))))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
Unsigned constraint
|
||||||
|
==================
|
||||||
|
|
||||||
|
value uint :: 255
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(source_file
|
||||||
|
(global_constant_declaration
|
||||||
|
(identifier)
|
||||||
|
(type
|
||||||
|
(builtin_type))
|
||||||
|
(expression
|
||||||
|
(integer))))
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
hide helper func() void {}
|
hide helper func() void {}
|
||||||
# <- keyword
|
# <- keyword
|
||||||
|
|
||||||
|
value uint :: 1
|
||||||
|
# ^^^^ type.builtin
|
||||||
|
|
||||||
main func() void {
|
main func() void {
|
||||||
_ = sizeof!(i32)
|
_ = sizeof!(i32)
|
||||||
# ^^^^^^ function.builtin
|
# ^^^^^^ function.builtin
|
||||||
|
|||||||
Reference in New Issue
Block a user