compiler: compile vox2 with ~14% fewer instructions; add -show-instructions (#31274)
* compiler: add -show-instructions to report retired instructions per stage -show-timings only prints wall time, which is noisy on a loaded machine. -show-instructions implies -show-timings and appends the retired CPU instructions of each stage (summed over all threads) and of the whole process, read from proc_pid_rusage on macOS. * transform: cache bare struct candidates and use interned names for registry lookups Receiver method resolution scanned every checker struct for each bare type name. Build a short-name -> qualified-struct index once per struct registry and invalidate it when generic templates are erased. Generic name checks rebuilt name-id indexes of the enum and transform registries whenever they grew; look the interned name up in the registry map directly instead. * markused: build interface dispatch keys once per scan The used-interface dispatch pass rebuilt every implementer method key on each worklist round. Precompute the groups and keys once, skip the pass when no new functions were marked, and enqueue IError keys once.
This commit is contained in:
1 parent
727f3cde79
commit
064d83671c
16 files changed
+356
-182
No files matched your search
@@ -159,6 +159,7 @@ const auto_complete_flags = [
|
||||
'-arch',
|
||||
'-assert',
|
||||
'-show-timings',
|
||||
'-show-instructions',
|
||||
'-show-asserts',
|
||||
'-check-syntax',
|
||||
'-check',
|
||||
|
||||
+4
-2
@@ -587,8 +587,10 @@ the V temporary directory by default; set `V3CACHE` to select another root, or p
|
||||
object cache. An explicit `-b c` binary build also retains the complete generated translation unit
|
||||
at `<output>.c` for codegen inspection. Use `-show-timings` to print elapsed time for each compiler
|
||||
stage and the total, including memory usage and cache metrics. It enables the same benchmark
|
||||
summary as `-v` without verbose compiler traces. For self-host builds, `-v` also reports
|
||||
time spent preparing markused, marking reachable symbols, and waiting for transform preparation.
|
||||
summary as `-v` without verbose compiler traces. `-show-instructions` also prints the retired
|
||||
CPU instructions of each stage and of the whole process (macOS only). For self-host builds, `-v`
|
||||
also reports time spent preparing markused, marking reachable symbols, and waiting for
|
||||
transform preparation.
|
||||
`-silent` and generated C output to stdout
|
||||
(`-o -`) suppress the summary. The benchmark output prints counts for parsed `.vh` and
|
||||
`.v` files and their total
|
||||
|
||||
+59
-6
@@ -82,6 +82,8 @@ mut:
|
||||
memory_monitor_interval i64
|
||||
memory_monitor_thread thread
|
||||
memory_monitor_started bool
|
||||
show_instructions bool
|
||||
last_instructions u64
|
||||
}
|
||||
|
||||
// new creates a new value for bench.
|
||||
@@ -129,6 +131,13 @@ pub fn (mut b Bench) set_quiet() {
|
||||
b.quiet = true
|
||||
}
|
||||
|
||||
// enable_instruction_counts adds the retired CPU instructions of every step
|
||||
// (summed over all threads) to the report, where the platform exposes them.
|
||||
pub fn (mut b Bench) enable_instruction_counts() {
|
||||
b.show_instructions = true
|
||||
b.last_instructions = current_instruction_count() or { 0 }
|
||||
}
|
||||
|
||||
// start_memory_monitor starts the compiler memory safety watchdog.
|
||||
pub fn (mut b Bench) start_memory_monitor() {
|
||||
if b.memory_limit_kb > 0 || !b.quiet {
|
||||
@@ -283,7 +292,7 @@ pub fn (b &Bench) current_step_time_us() i64 {
|
||||
// account for their full wall time.
|
||||
pub fn (mut b Bench) step_measured(name string, elapsed_us i64) {
|
||||
allocations := current_allocation_stats()
|
||||
b.report_step(name, false, elapsed_us, 0, 0, allocations.enabled, false)
|
||||
b.report_step(name, false, elapsed_us, 0, 0, allocations.enabled, false, '')
|
||||
}
|
||||
|
||||
// step_parallel records a pipeline step, appending "(parallel)" to its name
|
||||
@@ -291,9 +300,11 @@ pub fn (mut b Bench) step_measured(name string, elapsed_us i64) {
|
||||
pub fn (mut b Bench) step_parallel(name string, parallel bool) {
|
||||
elapsed_us := b.step_sw.elapsed().microseconds()
|
||||
allocations := current_allocation_stats()
|
||||
instructions := b.step_instructions()
|
||||
b.report_step(name, parallel, elapsed_us,
|
||||
allocations.allocation_count - b.last_allocation_count,
|
||||
allocations.allocated_bytes - b.last_allocated_bytes, allocations.enabled, true)
|
||||
allocations.allocated_bytes - b.last_allocated_bytes, allocations.enabled, true,
|
||||
instructions)
|
||||
b.finish_step_report()
|
||||
}
|
||||
|
||||
@@ -303,6 +314,9 @@ pub fn (mut b Bench) step_parts(parts []StepPart) {
|
||||
return
|
||||
}
|
||||
allocations := current_allocation_stats()
|
||||
// Instructions are counted per process, so they cannot be split between the
|
||||
// parts; the whole group's count is shown on its last part.
|
||||
instructions := b.step_instructions()
|
||||
total_allocation_count := allocations.allocation_count - b.last_allocation_count
|
||||
total_allocated_bytes := allocations.allocated_bytes - b.last_allocated_bytes
|
||||
mut total_us := i64(0)
|
||||
@@ -326,15 +340,20 @@ pub fn (mut b Bench) step_parts(parts []StepPart) {
|
||||
} else {
|
||||
u64(i64(total_allocated_bytes) * part.time_us / total_us)
|
||||
}
|
||||
part_instructions := if is_last && instructions.len > 0 {
|
||||
'${instructions} (${parts.len} parts)'
|
||||
} else {
|
||||
''
|
||||
}
|
||||
b.report_step(part.name, part.parallel, part.time_us, allocation_count, allocated_bytes,
|
||||
allocations.enabled, false)
|
||||
allocations.enabled, false, part_instructions)
|
||||
reported_allocation_count += allocation_count
|
||||
reported_allocated_bytes += allocated_bytes
|
||||
}
|
||||
b.finish_step_report()
|
||||
}
|
||||
|
||||
fn (mut b Bench) report_step(name string, parallel bool, elapsed_us i64, allocation_count u64, allocated_bytes u64, allocations_enabled bool, has_stage_peak bool) {
|
||||
fn (mut b Bench) report_step(name string, parallel bool, elapsed_us i64, allocation_count u64, allocated_bytes u64, allocations_enabled bool, has_stage_peak bool, instructions string) {
|
||||
label := if parallel { '${name} (parallel)' } else { name }
|
||||
ram_kb := current_rss_kb()
|
||||
stage_peak_ram_kb := if has_stage_peak { b.finish_stage_memory(ram_kb) } else { i64(0) }
|
||||
@@ -364,7 +383,7 @@ fn (mut b Bench) report_step(name string, parallel bool, elapsed_us i64, allocat
|
||||
''
|
||||
}
|
||||
if !b.quiet {
|
||||
println(' ${label:-20s} ${ms:8.2f} ms ${ram_mb:6.0f} MB RSS${footprint_suffix}${stage_peak_suffix} ${peak_ram_mb:6.0f} MB peak${allocation_suffix}')
|
||||
println(' ${label:-20s} ${ms:8.2f} ms ${ram_mb:6.0f} MB RSS${footprint_suffix}${stage_peak_suffix} ${peak_ram_mb:6.0f} MB peak${allocation_suffix}${instruction_suffix(instructions)}')
|
||||
}
|
||||
b.steps << Step{
|
||||
name: label
|
||||
@@ -382,9 +401,33 @@ fn (mut b Bench) finish_step_report() {
|
||||
after_report := current_allocation_stats()
|
||||
b.last_allocation_count = after_report.allocation_count
|
||||
b.last_allocated_bytes = after_report.allocated_bytes
|
||||
if b.show_instructions {
|
||||
b.last_instructions = current_instruction_count() or { 0 }
|
||||
}
|
||||
b.step_sw.restart()
|
||||
}
|
||||
|
||||
// step_instructions returns the formatted instruction count of the current
|
||||
// step, or an empty string when instruction counts are off or unavailable.
|
||||
fn (b &Bench) step_instructions() string {
|
||||
if !b.show_instructions {
|
||||
return ''
|
||||
}
|
||||
count := current_instruction_count() or { return '' }
|
||||
return format_instructions(count - b.last_instructions)
|
||||
}
|
||||
|
||||
fn format_instructions(count u64) string {
|
||||
return '${f64(count) / 1e9:8.3f} G instructions'
|
||||
}
|
||||
|
||||
fn instruction_suffix(instructions string) string {
|
||||
if instructions.len == 0 {
|
||||
return ''
|
||||
}
|
||||
return ' ${instructions}'
|
||||
}
|
||||
|
||||
fn physical_footprint_suffix(memory LimitMemory) string {
|
||||
if memory.metric != 'physical footprint' {
|
||||
return ''
|
||||
@@ -443,7 +486,17 @@ pub fn (b &Bench) print_report() {
|
||||
return
|
||||
}
|
||||
total_ms := f64(b.total_sw.elapsed().microseconds()) / 1000.0
|
||||
println(' ${'total':-20s} ${total_ms:8.2f} ms')
|
||||
mut total_instructions := ''
|
||||
if b.show_instructions {
|
||||
// The process total includes startup and everything between the steps,
|
||||
// so it matches what an external counter such as `/usr/bin/time -l` reports.
|
||||
total_instructions = if count := current_instruction_count() {
|
||||
' ${format_instructions(count)} (process)'
|
||||
} else {
|
||||
' instruction counts are unavailable on this platform'
|
||||
}
|
||||
}
|
||||
println(' ${'total':-20s} ${total_ms:8.2f} ms${total_instructions}')
|
||||
if b.metrics.len > 0 {
|
||||
println(' metrics:')
|
||||
for metric in b.metrics {
|
||||
|
||||
@@ -148,3 +148,33 @@ fn test_shorten_home_path() {
|
||||
assert shorten_home_path('${home}_other/code/project/main.v') == '${home}_other/code/project/main.v'
|
||||
assert shorten_home_path('/tmp/project/main.v') == '/tmp/project/main.v'
|
||||
}
|
||||
|
||||
fn test_instruction_count_formatting() {
|
||||
assert format_instructions(1_234_567_890) == ' 1.235 G instructions'
|
||||
assert instruction_suffix('') == ''
|
||||
assert instruction_suffix('1 G instructions') == ' 1 G instructions'
|
||||
}
|
||||
|
||||
fn test_instruction_counts_are_off_by_default() {
|
||||
b := new()
|
||||
assert !b.show_instructions
|
||||
assert b.step_instructions() == ''
|
||||
}
|
||||
|
||||
fn test_enable_instruction_counts() {
|
||||
mut b := new()
|
||||
b.set_quiet()
|
||||
b.enable_instruction_counts()
|
||||
assert b.show_instructions
|
||||
$if macos {
|
||||
start := current_instruction_count() or { 0 }
|
||||
assert start > 0
|
||||
assert b.last_instructions > 0
|
||||
assert b.last_instructions <= start
|
||||
assert b.step_instructions().ends_with(' G instructions')
|
||||
b.step('work')
|
||||
assert b.last_instructions >= start
|
||||
} $else {
|
||||
assert b.step_instructions() == ''
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
module bench
|
||||
|
||||
import os
|
||||
|
||||
const rusage_info_v4 = 4
|
||||
|
||||
// current_instruction_count returns the retired CPU instructions of the whole
|
||||
// process (all threads) since it started.
|
||||
fn current_instruction_count() ?u64 {
|
||||
$if macos || ios {
|
||||
mut usage := V3RusageInfo{}
|
||||
if C.proc_pid_rusage(os.getpid(), rusage_info_v4, &usage) == 0 && usage.ri_instructions > 0 {
|
||||
return usage.ri_instructions
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module bench
|
||||
|
||||
// current_instruction_count returns none: retired instruction counters are
|
||||
// only read on macOS.
|
||||
fn current_instruction_count() ?u64 {
|
||||
return none
|
||||
}
|
||||
@@ -14,6 +14,32 @@ struct V3RusageInfo {
|
||||
ri_phys_footprint u64
|
||||
ri_proc_start_abstime u64
|
||||
ri_proc_exit_abstime u64
|
||||
// The fields below are only filled by RUSAGE_INFO_V4 and later.
|
||||
ri_child_user_time u64
|
||||
ri_child_system_time u64
|
||||
ri_child_pkg_idle_wkups u64
|
||||
ri_child_interrupt_wkups u64
|
||||
ri_child_pageins u64
|
||||
ri_child_elapsed_abstime u64
|
||||
ri_diskio_bytesread u64
|
||||
ri_diskio_byteswritten u64
|
||||
ri_cpu_time_qos_default u64
|
||||
ri_cpu_time_qos_maintenance u64
|
||||
ri_cpu_time_qos_background u64
|
||||
ri_cpu_time_qos_utility u64
|
||||
ri_cpu_time_qos_legacy u64
|
||||
ri_cpu_time_qos_user_initiated u64
|
||||
ri_cpu_time_qos_user_interactive u64
|
||||
ri_billed_system_time u64
|
||||
ri_serviced_system_time u64
|
||||
ri_logical_writes u64
|
||||
ri_lifetime_max_phys_footprint u64
|
||||
ri_instructions u64
|
||||
ri_cycles u64
|
||||
ri_billed_energy u64
|
||||
ri_serviced_energy u64
|
||||
ri_interval_max_phys_footprint u64
|
||||
ri_runnable_time u64
|
||||
}
|
||||
|
||||
@[c_extern]
|
||||
|
||||
+10
-3
@@ -4319,7 +4319,7 @@ fn v3_crun_build_identity(state &V3ModuleCacheState, prefs &pref.Preferences, us
|
||||
}
|
||||
|
||||
fn cli_usage() string {
|
||||
return 'usage: v3 [run|crun|test] <file.v|directory> [options]\n' + ' -o <output> output binary or C file\n' + ' -b <c|fastc|arm64|wasm|eval> backend\n' + ' -os <name> -arch <name> target platform\n' + ' -cc <compiler> C compiler executable\n' + ' -cflags <flags> extra C compiler options\n' + ' -ldflags <flags> extra options appended to the link command\n' + ' -thread-stack-size <bytes> spawned-thread stack size\n' + ' -prod -c99 -shared -strict C build modes\n' + ' -v verbose stage profiling\n' + ' -show-timings compiler stage timings without verbose traces\n' + ' -silent suppress benchmark output\n' + ' -showcc print C compiler commands\n' + ' -trace-calls trace function entries to stderr\n' + ' -trace-fns <patterns> restrict tracing to functions or modules\n' + ' -race detect data races at runtime (ThreadSanitizer)\n' + ' -profile [file] write V1-compatible function profile data\n' + ' -profile-fns <names> profile only named functions and their callees\n' + ' -profile-no-inline omit @[inline] functions from the profile\n' + ' -no-memory-limit disable the 10176 MiB user-build memory safety limit\n' + ' -memory-limit <size> set a custom user-build memory safety limit\n to specify unit append K|M|G (or k|m|g) (default: M)\n' + ' -d <name> compile-time define'
|
||||
return 'usage: v3 [run|crun|test] <file.v|directory> [options]\n' + ' -o <output> output binary or C file\n' + ' -b <c|fastc|arm64|wasm|eval> backend\n' + ' -os <name> -arch <name> target platform\n' + ' -cc <compiler> C compiler executable\n' + ' -cflags <flags> extra C compiler options\n' + ' -ldflags <flags> extra options appended to the link command\n' + ' -thread-stack-size <bytes> spawned-thread stack size\n' + ' -prod -c99 -shared -strict C build modes\n' + ' -v verbose stage profiling\n' + ' -show-timings compiler stage timings without verbose traces\n' + ' -show-instructions -show-timings plus retired CPU instructions per stage (macOS)\n' + ' -silent suppress benchmark output\n' + ' -showcc print C compiler commands\n' + ' -trace-calls trace function entries to stderr\n' + ' -trace-fns <patterns> restrict tracing to functions or modules\n' + ' -race detect data races at runtime (ThreadSanitizer)\n' + ' -profile [file] write V1-compatible function profile data\n' + ' -profile-fns <names> profile only named functions and their callees\n' + ' -profile-no-inline omit @[inline] functions from the profile\n' + ' -no-memory-limit disable the 10176 MiB user-build memory safety limit\n' + ' -memory-limit <size> set a custom user-build memory safety limit\n to specify unit append K|M|G (or k|m|g) (default: M)\n' + ' -d <name> compile-time define'
|
||||
}
|
||||
|
||||
fn shared_library_postfix(target_os string) string {
|
||||
@@ -4762,8 +4762,8 @@ fn v3_fast_build_tree_signature_into(root string, mut entries strings.Builder) {
|
||||
fn v3_unchanged_build_key(prefs &pref.Preferences, c_compiler string, bin_file string) string {
|
||||
mut output_args := []string{}
|
||||
for arg in os.args[1..] {
|
||||
if arg in ['-nocache', '--no-cache', '-v', '-show-timings', '-silent', '-showcc',
|
||||
'-show-c-output', '-no-memory-limit'] || arg.starts_with('-v3-internal-') {
|
||||
if arg in ['-nocache', '--no-cache', '-v', '-show-timings', '-show-instructions', '-silent',
|
||||
'-showcc', '-show-c-output', '-no-memory-limit'] || arg.starts_with('-v3-internal-') {
|
||||
continue
|
||||
}
|
||||
output_args << arg
|
||||
@@ -11215,6 +11215,7 @@ pub fn run(args []string) {
|
||||
mut ownership_mode := false
|
||||
mut verbose := false
|
||||
mut show_timings := false
|
||||
mut show_instructions := false
|
||||
mut silent := false
|
||||
mut deferred_implicit_tcc_warning := ''
|
||||
mut skip_notices := false
|
||||
@@ -11805,6 +11806,10 @@ pub fn run(args []string) {
|
||||
} else if args[i] == '-show-timings' {
|
||||
show_timings = true
|
||||
i++
|
||||
} else if args[i] == '-show-instructions' {
|
||||
show_timings = true
|
||||
show_instructions = true
|
||||
i++
|
||||
} else if args[i] in ['-new-generic-solver', '-progress', '-use-os-system-to-run'] {
|
||||
// v3 uses its current generic solver without a legacy selection switch.
|
||||
// `-progress` selects a reporter in the test runner, not in the compiler.
|
||||
@@ -12430,6 +12435,8 @@ pub fn run(args []string) {
|
||||
driver_sw := time.new_stopwatch()
|
||||
if !(verbose || show_timings) || silent || c_to_stdout {
|
||||
b.set_quiet()
|
||||
} else if show_instructions {
|
||||
b.enable_instruction_counts()
|
||||
}
|
||||
if no_memory_limit {
|
||||
b.disable_memory_limit()
|
||||
|
||||
@@ -342,6 +342,13 @@ NB: the build flags are shared with the run command too:
|
||||
without verbose compiler traces. -silent suppresses this summary, as does writing
|
||||
generated C to stdout with -o -.
|
||||
|
||||
-show-instructions
|
||||
Like -show-timings, and also prints the retired CPU instructions of each compiler
|
||||
stage, summed over all threads, plus the total for the whole process. Instruction
|
||||
counts are less sensitive to machine load than elapsed time, which makes them useful
|
||||
when comparing compiler changes. They are read from the kernel on macOS; on other
|
||||
systems, only the timings are printed.
|
||||
|
||||
Related to -show-timings, is the ability to compile a special instrumented
|
||||
v compiler with this command:
|
||||
`v -d time_parsing -d time_checking -d time_cgening -d time_v self`
|
||||
|
||||
+105
-33
@@ -945,7 +945,7 @@ fn mark_used_with_test_files(a &flat.FlatAst, tc &types.TypeChecker, test_files
|
||||
// Interface method values and other roots can enter the queue as abstract
|
||||
// dispatch keys that have no function declaration of their own. Seed their
|
||||
// concrete implementations before the declaration-driven BFS skips those keys.
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut reachable_helper_scan.interface_dispatch_methods)
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut reachable_helper_scan.interface_dispatch)
|
||||
// Interface dispatch reachability: calling an interface method `Foo.m` may
|
||||
// dispatch to any concrete `T.m` for a type `T` that implements `Foo`. Those
|
||||
// concrete methods are only referenced from the generated dispatch switch, so
|
||||
@@ -1394,52 +1394,125 @@ fn mark_used_with_test_files(a &flat.FlatAst, tc &types.TypeChecker, test_files
|
||||
return used, uses_generics
|
||||
}
|
||||
|
||||
// Each used dispatch method expands a fixed declaration graph during markused.
|
||||
// Remember completed methods while still discovering methods reached by later bodies.
|
||||
fn enqueue_used_interface_dispatch_implementers(tc &types.TypeChecker, mut used map[string]bool, mut queue []string, mut processed map[string]bool) bool {
|
||||
mut added := false
|
||||
for iface_name_source_id in tc.interface_names.keys() {
|
||||
iface_name := flat.source_type_name(iface_name_source_id)
|
||||
iface_name_id := iface_name_source_id
|
||||
// InterfaceDispatchScan remembers the dispatch keys of every abstract interface method,
|
||||
// and which methods already had their implementers enqueued. The checker is fixed during
|
||||
// markused and `used` only grows, so the keys are built once per scan.
|
||||
struct InterfaceDispatchScan {
|
||||
mut:
|
||||
processed map[string]bool
|
||||
groups []InterfaceDispatchGroup
|
||||
ierror_keys []string
|
||||
built bool
|
||||
ierror_keys_enqueued bool
|
||||
// used.len after the last call that found nothing new; an unchanged len means an unchanged set.
|
||||
settled_used_len int = -1
|
||||
}
|
||||
|
||||
struct InterfaceDispatchGroup {
|
||||
name_id u32
|
||||
is_ierror bool
|
||||
mut:
|
||||
methods []InterfaceDispatchMethod
|
||||
pending int
|
||||
}
|
||||
|
||||
struct InterfaceDispatchMethod {
|
||||
name string
|
||||
key string
|
||||
c_key string
|
||||
short_key string
|
||||
mut:
|
||||
done bool
|
||||
}
|
||||
|
||||
fn (mut scan InterfaceDispatchScan) build(tc &types.TypeChecker) {
|
||||
scan.built = true
|
||||
for iface_name_id in tc.interface_names.keys() {
|
||||
iface_name := flat.source_type_name(iface_name_id)
|
||||
is_ierror := markused_is_ierror_interface_name(iface_name_id)
|
||||
if is_ierror {
|
||||
for method in ['msg', 'code'] {
|
||||
dispatch_key := '${iface_name}.${method}'
|
||||
scan.ierror_keys << dispatch_key
|
||||
scan.ierror_keys << markused_c_name(dispatch_key)
|
||||
}
|
||||
}
|
||||
methods := tc.interface_abstract_method_names(iface_name_id)
|
||||
if methods.len == 0 {
|
||||
continue
|
||||
}
|
||||
mut used_methods := []string{cap: methods.len}
|
||||
short_name := iface_name.all_after_last('.')
|
||||
mut group := InterfaceDispatchGroup{
|
||||
name_id: iface_name_id
|
||||
is_ierror: is_ierror
|
||||
methods: []InterfaceDispatchMethod{cap: methods.len}
|
||||
pending: methods.len
|
||||
}
|
||||
for method in methods {
|
||||
dispatch_key := '${iface_name}.${method}'
|
||||
if processed[dispatch_key] {
|
||||
group.methods << InterfaceDispatchMethod{
|
||||
name: method
|
||||
key: dispatch_key
|
||||
c_key: markused_c_name(dispatch_key)
|
||||
short_key: '${short_name}.${method}'
|
||||
}
|
||||
}
|
||||
scan.groups << group
|
||||
}
|
||||
}
|
||||
|
||||
// Each used dispatch method expands a fixed declaration graph during markused.
|
||||
// Remember completed methods while still discovering methods reached by later bodies.
|
||||
fn enqueue_used_interface_dispatch_implementers(tc &types.TypeChecker, mut used map[string]bool, mut queue []string, mut scan InterfaceDispatchScan) bool {
|
||||
if !scan.built {
|
||||
scan.build(tc)
|
||||
}
|
||||
start_len := used.len
|
||||
if start_len == scan.settled_used_len {
|
||||
return false
|
||||
}
|
||||
mut added := false
|
||||
for mut group in scan.groups {
|
||||
if group.pending == 0 {
|
||||
continue
|
||||
}
|
||||
mut used_methods := []int{}
|
||||
for i, method in group.methods {
|
||||
if method.done || scan.processed[method.key] {
|
||||
continue
|
||||
}
|
||||
dispatch_c_key := markused_c_name(dispatch_key)
|
||||
short_dispatch_key := '${iface_name.all_after_last('.')}.${method}'
|
||||
if dispatch_key in used || dispatch_c_key in used || short_dispatch_key in used {
|
||||
used_methods << method
|
||||
if method.key in used || method.c_key in used || method.short_key in used {
|
||||
used_methods << i
|
||||
}
|
||||
}
|
||||
if used_methods.len == 0 {
|
||||
continue
|
||||
}
|
||||
impls := if markused_is_ierror_interface_name(iface_name_id) {
|
||||
impls := if group.is_ierror {
|
||||
tc.ierror_impl_names()
|
||||
} else {
|
||||
markused_interface_impl_names(iface_name_id, tc)
|
||||
markused_interface_impl_names(group.name_id, tc)
|
||||
}
|
||||
if impls.len == 0 {
|
||||
continue
|
||||
}
|
||||
for method in used_methods {
|
||||
for i in used_methods {
|
||||
method_name := group.methods[i].name
|
||||
for impl in impls {
|
||||
for alias in interface_implementer_method_aliases(impl, method, tc) {
|
||||
for alias in interface_implementer_method_aliases(impl, method_name, tc) {
|
||||
if enqueue(alias, mut used, mut queue) {
|
||||
added = true
|
||||
}
|
||||
}
|
||||
enqueue_implicit_interface_str_helpers_for_impl(iface_name_id, method, impl, tc, mut used, mut queue)
|
||||
enqueue_implicit_interface_str_helpers_for_impl(group.name_id, method_name,
|
||||
impl, tc, mut used, mut queue)
|
||||
}
|
||||
processed['${iface_name}.${method}'] = true
|
||||
scan.processed[group.methods[i].key] = true
|
||||
group.methods[i].done = true
|
||||
group.pending--
|
||||
}
|
||||
}
|
||||
scan.settled_used_len = if used.len == start_len { start_len } else { -1 }
|
||||
return added
|
||||
}
|
||||
|
||||
@@ -2930,7 +3003,7 @@ mut:
|
||||
|
||||
channel_stringify_cache map[string]&MarkusedTypeResults
|
||||
ierror_equality_cache map[u32]int
|
||||
interface_dispatch_methods map[string]bool
|
||||
interface_dispatch InterfaceDispatchScan
|
||||
map_type_cache map[u32]int
|
||||
unknown_map_type_cache map[string]int
|
||||
auto_str_skipped_fields map[u32]&MarkusedSkippedFields
|
||||
@@ -3483,7 +3556,7 @@ fn (mut scan RuntimeHelpersScan) enqueue_nodes(a &flat.FlatAst, tc &types.TypeCh
|
||||
enqueue('f32__eq_epsilon', mut used, mut queue)
|
||||
}
|
||||
if scan.needs_ierror_equality_dispatch {
|
||||
enqueue_ierror_equality_dispatch_helpers(tc, mut used, mut queue, mut scan.interface_dispatch_methods)
|
||||
enqueue_ierror_equality_dispatch_helpers(tc, mut used, mut queue, mut scan.interface_dispatch)
|
||||
}
|
||||
if scan.needs_shared_runtime && !scan.enqueued_groups.has(.shared) {
|
||||
scan.enqueued_groups.set(.shared)
|
||||
@@ -3562,19 +3635,18 @@ fn markused_is_builtin_closure_global(a &flat.FlatAst, tc &types.TypeChecker, no
|
||||
&& os.dir(file.name).replace('\\', '/').ends_with('vlib/builtin/closure')
|
||||
}
|
||||
|
||||
fn enqueue_ierror_equality_dispatch_helpers(tc &types.TypeChecker, mut used map[string]bool, mut queue []string, mut processed_dispatches map[string]bool) {
|
||||
for iface_name_source_id in tc.interface_names.keys() {
|
||||
iface_name := flat.source_type_name(iface_name_source_id)
|
||||
if !markused_is_ierror_interface_name(iface_name_source_id) {
|
||||
continue
|
||||
}
|
||||
for method in ['msg', 'code'] {
|
||||
dispatch_key := '${iface_name}.${method}'
|
||||
enqueue(dispatch_key, mut used, mut queue)
|
||||
enqueue(markused_c_name(dispatch_key), mut used, mut queue)
|
||||
fn enqueue_ierror_equality_dispatch_helpers(tc &types.TypeChecker, mut used map[string]bool, mut queue []string, mut scan InterfaceDispatchScan) {
|
||||
if !scan.built {
|
||||
scan.build(tc)
|
||||
}
|
||||
// `used` only grows, so the IError dispatch keys stay enqueued once added.
|
||||
if !scan.ierror_keys_enqueued {
|
||||
scan.ierror_keys_enqueued = true
|
||||
for key in scan.ierror_keys {
|
||||
enqueue(key, mut used, mut queue)
|
||||
}
|
||||
}
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut processed_dispatches)
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut scan)
|
||||
}
|
||||
|
||||
fn markused_type_equality_uses_ierror(typ types.Type, tc &types.TypeChecker, mut cache map[u32]int) bool {
|
||||
|
||||
@@ -669,23 +669,23 @@ fn main() {}
|
||||
')
|
||||
mut used := map[string]bool{}
|
||||
mut queue := []string{}
|
||||
mut processed := map[string]bool{}
|
||||
assert !enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut processed)
|
||||
assert processed.len == 0
|
||||
mut scan := InterfaceDispatchScan{}
|
||||
assert !enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut scan)
|
||||
assert scan.processed.len == 0
|
||||
used['Reader.first'] = true
|
||||
assert enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut processed)
|
||||
assert enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut scan)
|
||||
assert used['Item.first']
|
||||
assert !used['Item.second']
|
||||
assert processed['Reader.first']
|
||||
assert !processed['Reader.second']
|
||||
assert scan.processed['Reader.first']
|
||||
assert !scan.processed['Reader.second']
|
||||
first_len := queue.len
|
||||
assert !enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut processed)
|
||||
assert !enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut scan)
|
||||
assert queue.len == first_len
|
||||
// A later body can introduce a second abstract dispatch after the first one was cached.
|
||||
used['Reader.second'] = true
|
||||
enqueue_ierror_equality_dispatch_helpers(tc, mut used, mut queue, mut processed)
|
||||
enqueue_ierror_equality_dispatch_helpers(tc, mut used, mut queue, mut scan)
|
||||
assert used['Item.second']
|
||||
assert processed['Reader.second']
|
||||
assert scan.processed['Reader.second']
|
||||
}
|
||||
|
||||
fn test_interface_dispatch_cache_keeps_implicit_stringification_dependencies() {
|
||||
@@ -705,16 +705,16 @@ fn main() {
|
||||
')
|
||||
mut used := map[string]bool{}
|
||||
mut queue := []string{}
|
||||
mut processed := map[string]bool{}
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut processed)
|
||||
assert !processed['Printable.str']
|
||||
mut scan := InterfaceDispatchScan{}
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut scan)
|
||||
assert !scan.processed['Printable.str']
|
||||
used['Printable.str'] = true
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut processed)
|
||||
assert processed['Printable.str']
|
||||
enqueue_used_interface_dispatch_implementers(tc, mut used, mut queue, mut scan)
|
||||
assert scan.processed['Printable.str']
|
||||
assert used['f64.str']
|
||||
assert used['strconv__f64_to_str_l']
|
||||
first_len := queue.len
|
||||
enqueue_ierror_equality_dispatch_helpers(tc, mut used, mut queue, mut processed)
|
||||
enqueue_ierror_equality_dispatch_helpers(tc, mut used, mut queue, mut scan)
|
||||
assert queue.len == first_len
|
||||
}
|
||||
|
||||
|
||||
@@ -1578,7 +1578,7 @@ fn (t &Transformer) generic_checker_struct_name_known(name_id u32) bool {
|
||||
if t.generic_type_indexes_ready && t.generic_checker_struct_index_count == t.tc.structs.len {
|
||||
return t.generic_checker_struct_names[name_id]
|
||||
}
|
||||
return t.refresh_checker_struct_type_name_index().struct_names[name_id]
|
||||
return name_id in t.tc.structs
|
||||
}
|
||||
|
||||
fn (t &Transformer) comptime_method_requested_module(raw_id u32) string {
|
||||
|
||||
+40
-7
@@ -540,6 +540,43 @@ fn (mut cache ReceiverMethodCache) set_front(slot int, receiver_type_id u32, met
|
||||
cache.front_states[slot] = if found { i8(1) } else { i8(-1) }
|
||||
}
|
||||
|
||||
// bare_struct_candidates returns the module-qualified, non-generic structs whose
|
||||
// short name is `short_id`. Entries can be stale after template erasure, so
|
||||
// callers check that each name is still in `t.tc.structs`.
|
||||
fn (t &Transformer) bare_struct_candidates(short_id u32) []u32 {
|
||||
if isnil(t.receiver_method_cache) {
|
||||
return t.collect_bare_struct_candidates(short_id)
|
||||
}
|
||||
mut cache := t.receiver_method_cache
|
||||
tc_ptr := voidptr(t.tc)
|
||||
if cache.bare_structs_tc != tc_ptr || cache.bare_structs_len != t.tc.structs.len {
|
||||
cache.bare_structs.clear()
|
||||
for sname_id, _ in t.tc.structs {
|
||||
sname_shape := flat.source_type_shape(sname_id)
|
||||
if !sname_shape.has(.dotted) || sname_shape.has(.bracketed) {
|
||||
continue
|
||||
}
|
||||
short := flat.source_type_syntax(sname_id).unqualified()
|
||||
cache.bare_structs[short] << sname_id
|
||||
}
|
||||
cache.bare_structs_tc = tc_ptr
|
||||
cache.bare_structs_len = t.tc.structs.len
|
||||
}
|
||||
return cache.bare_structs[short_id] or { []u32{} }
|
||||
}
|
||||
|
||||
fn (t &Transformer) collect_bare_struct_candidates(short_id u32) []u32 {
|
||||
mut ids := []u32{}
|
||||
for sname_id, _ in t.tc.structs {
|
||||
sname_shape := flat.source_type_shape(sname_id)
|
||||
if sname_shape.has(.dotted) && !sname_shape.has(.bracketed)
|
||||
&& flat.source_type_syntax(sname_id).unqualified() == short_id {
|
||||
ids << sname_id
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
fn (t &Transformer) resolve_receiver_method_for_type_uncached(receiver_type_id u32, method string) ?string {
|
||||
clean_type_id := t.trim_pointer_type(receiver_type_id)
|
||||
clean_type := flat.source_type_name(clean_type_id)
|
||||
@@ -649,15 +686,11 @@ fn (t &Transformer) resolve_receiver_method_for_type_uncached(receiver_type_id u
|
||||
if !clean_shape.has(.dotted) && !clean_shape.has(.bracketed) && !isnil(t.tc)
|
||||
&& !t.tc.is_locally_declared_bare_type(clean_type_id) {
|
||||
mut matched := ''
|
||||
for sname_source_id, _ in t.tc.structs {
|
||||
sname := flat.source_type_name(sname_source_id)
|
||||
sname_id := sname_source_id
|
||||
sname_shape := flat.source_type_shape(sname_id)
|
||||
if !sname_shape.has(.dotted) || sname_shape.has(.bracketed)
|
||||
|| flat.source_type_syntax(sname_id).unqualified() != clean_type_id {
|
||||
for sname_id in t.bare_struct_candidates(clean_type_id) {
|
||||
if sname_id !in t.tc.structs {
|
||||
continue
|
||||
}
|
||||
qmethod := '${sname}.${method}'
|
||||
qmethod := '${flat.source_type_name(sname_id)}.${method}'
|
||||
if t.is_known_fn_name(qmethod) {
|
||||
if matched != '' && matched != qmethod {
|
||||
matched = ''
|
||||
|
||||
@@ -19,7 +19,6 @@ fn test_generic_type_name_indexes_refresh_after_late_declarations() {
|
||||
assert t.generic_concrete_name_known(flat.source_type_id(name))
|
||||
assert t.type_name_is_declared_id(flat.source_type_id(name))
|
||||
}
|
||||
assert t.generic_type_name_index_cache.checker_count == t.generic_checker_concrete_name_count()
|
||||
assert t.generic_checker_sum_name_known(flat.source_type_id('late.Choice'))
|
||||
assert t.generic_checker_interface_name_known(flat.source_type_id('late.Reader'))
|
||||
assert !t.generic_checker_sum_name_known(flat.source_type_id('late.MoreChoice'))
|
||||
@@ -47,7 +46,6 @@ fn test_generic_type_name_indexes_refresh_after_late_declarations() {
|
||||
assert t.type_name_is_declared_id(flat.source_type_id(name))
|
||||
}
|
||||
assert !t.type_name_is_declared_id(flat.source_type_id('late.Missing'))
|
||||
assert t.generic_type_name_index_cache.transform_count == t.structs.len + t.sum_types.ids.len + t.enum_types.len
|
||||
}
|
||||
|
||||
fn test_generic_type_name_index_refresh_is_private_to_each_worker() {
|
||||
@@ -67,8 +65,6 @@ fn test_generic_type_name_index_refresh_is_private_to_each_worker() {
|
||||
assert !worker.generic_concrete_name_known(flat.source_type_id('RootRecord'))
|
||||
assert t.generic_concrete_name_known(flat.source_type_id('RootRecord'))
|
||||
assert !t.generic_concrete_name_known(flat.source_type_id('WorkerRecord'))
|
||||
assert worker.generic_type_name_index_cache.checker_count == 1
|
||||
assert t.generic_type_name_index_cache.checker_count == 2
|
||||
}
|
||||
|
||||
fn test_generic_type_name_indexes_refresh_struct_enum_and_parameter_views() {
|
||||
|
||||
+28
-111
@@ -3260,6 +3260,9 @@ fn (mut t Transformer) materialize_generic_structs(erase_templates bool) {
|
||||
t.tc.params_structs.delete(decl_id)
|
||||
t.tc.unregister_short_type_name(decl_id)
|
||||
}
|
||||
if !isnil(t.receiver_method_cache) {
|
||||
t.receiver_method_cache.bare_structs_len = -1
|
||||
}
|
||||
// Refresh once after both registries have finished removing templates.
|
||||
t.erase_generic_sum_templates(sum_decls)
|
||||
}
|
||||
@@ -10371,28 +10374,9 @@ fn (t &Transformer) generic_enum_type_name_known(name_id u32) bool {
|
||||
if t.generic_type_indexes_ready && t.generic_enum_index_count == count {
|
||||
return t.generic_enum_type_names[name_id]
|
||||
}
|
||||
return t.refresh_enum_type_name_index().enum_names[name_id]
|
||||
}
|
||||
|
||||
fn (t &Transformer) refresh_enum_type_name_index() &GenericTypeNameIndexCache {
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
count := t.enum_types.len + if isnil(t.tc) { 0 } else { t.tc.enum_names.len }
|
||||
if cache.enum_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
mut checker_names := map[u32]bool{}
|
||||
for name_id, _ in t.enum_types { names[name_id] = true }
|
||||
if !isnil(t.tc) {
|
||||
for name_source_id, _ in t.tc.enum_names {
|
||||
name_id := name_source_id
|
||||
names[name_id] = true
|
||||
checker_names[name_id] = true
|
||||
}
|
||||
}
|
||||
cache.enum_names = names.move()
|
||||
cache.checker_enum_names = checker_names.move()
|
||||
cache.enum_count = count
|
||||
}
|
||||
return cache
|
||||
// Late registrations make the prepared index stale; the registries are
|
||||
// keyed by interned ids, so look the id up directly.
|
||||
return name_id in t.enum_types || (!isnil(t.tc) && name_id in t.tc.enum_names)
|
||||
}
|
||||
|
||||
fn (t &Transformer) refresh_checker_struct_type_name_index() &GenericTypeNameIndexCache {
|
||||
@@ -10425,14 +10409,7 @@ fn (t &Transformer) generic_checker_sum_name_known(name_id u32) bool {
|
||||
if t.generic_type_indexes_ready && t.generic_checker_sum_index_count == count {
|
||||
return t.generic_checker_sum_names[name_id]
|
||||
}
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
if cache.sum_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
for name_source_id, _ in t.tc.sum_types.ids { names[name_source_id] = true }
|
||||
cache.sum_names = names.move()
|
||||
cache.sum_count = count
|
||||
}
|
||||
return cache.sum_names[name_id]
|
||||
return name_id in t.tc.sum_types.ids
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_checker_interface_name_known(name_id u32) bool {
|
||||
@@ -10441,14 +10418,7 @@ fn (t &Transformer) generic_checker_interface_name_known(name_id u32) bool {
|
||||
if t.generic_type_indexes_ready && t.generic_checker_interface_index_count == count {
|
||||
return t.generic_checker_interface_names[name_id]
|
||||
}
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
if cache.interface_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
for name_source_id, _ in t.tc.interface_names { names[name_source_id] = true }
|
||||
cache.interface_names = names.move()
|
||||
cache.interface_count = count
|
||||
}
|
||||
return cache.interface_names[name_id]
|
||||
return name_id in t.tc.interface_names
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_exact_alias_name_known(name_id u32) bool {
|
||||
@@ -10457,59 +10427,18 @@ fn (t &Transformer) generic_exact_alias_name_known(name_id u32) bool {
|
||||
if t.generic_type_indexes_ready && t.generic_exact_alias_names.len == count {
|
||||
return t.generic_exact_alias_names[name_id]
|
||||
}
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
if cache.alias_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
for name_source_id, _ in t.tc.type_aliases.ids { names[name_source_id] = true }
|
||||
cache.alias_names = names.move()
|
||||
cache.alias_count = count
|
||||
}
|
||||
return cache.alias_names[name_id]
|
||||
return name_id in t.tc.type_aliases.ids
|
||||
}
|
||||
|
||||
// GenericTypeNameIndexCache belongs to one transformer or worker. Late
|
||||
// declarations invalidate the prepared indexes; rebuild their numeric views
|
||||
// once per registry size change instead of re-interning every key per lookup.
|
||||
// GenericTypeNameIndexCache belongs to one transformer or worker. Late struct
|
||||
// declarations invalidate the prepared indexes; rebuild the short-name view
|
||||
// once per registry size change. Exact-name checks look the interned spelling
|
||||
// up in the registry directly instead.
|
||||
struct GenericTypeNameIndexCache {
|
||||
mut:
|
||||
checker_count int = -1
|
||||
checker_names map[u32]bool
|
||||
sum_count int = -1
|
||||
sum_names map[u32]bool
|
||||
interface_count int = -1
|
||||
interface_names map[u32]bool
|
||||
alias_count int = -1
|
||||
alias_names map[u32]bool
|
||||
enum_count int = -1
|
||||
enum_names map[u32]bool
|
||||
checker_enum_names map[u32]bool
|
||||
struct_count int = -1
|
||||
struct_names map[u32]bool
|
||||
struct_short_names map[u32]bool
|
||||
parameter_count int = -1
|
||||
parameter_names map[u32]bool
|
||||
transform_count int = -1
|
||||
transform_names map[u32]bool
|
||||
main_structs map[u32]bool
|
||||
}
|
||||
|
||||
fn (t &Transformer) refresh_transform_type_name_index() &GenericTypeNameIndexCache {
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
count := t.structs.len + t.sum_types.ids.len + t.enum_types.len
|
||||
if cache.transform_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
mut main_structs := map[u32]bool{}
|
||||
for name_id, info in t.structs {
|
||||
names[name_id] = true
|
||||
if info.module in ['', 'main'] { main_structs[name_id] = true }
|
||||
}
|
||||
for name_id, _ in t.sum_types.ids { names[name_id] = true }
|
||||
for name_id, _ in t.enum_types { names[name_id] = true }
|
||||
cache.transform_names = names.move()
|
||||
cache.main_structs = main_structs.move()
|
||||
cache.transform_count = count
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_transform_concrete_name_known(name_id u32) bool {
|
||||
@@ -10517,7 +10446,7 @@ fn (t &Transformer) generic_transform_concrete_name_known(name_id u32) bool {
|
||||
&& t.generic_transform_concrete_index_count == t.structs.len + t.sum_types.ids.len + t.enum_types.len {
|
||||
return t.generic_transform_concrete_type_names[name_id]
|
||||
}
|
||||
return t.refresh_transform_type_name_index().transform_names[name_id]
|
||||
return name_id in t.structs || name_id in t.sum_types.ids || name_id in t.enum_types
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_resolved_concrete_name_known(name_id u32) bool {
|
||||
@@ -10526,21 +10455,12 @@ fn (t &Transformer) generic_resolved_concrete_name_known(name_id u32) bool {
|
||||
|
||||
fn (t &Transformer) generic_concrete_name_known(name_id u32) bool {
|
||||
if isnil(t.tc) { return false }
|
||||
count := t.generic_checker_concrete_name_count()
|
||||
if t.generic_type_indexes_ready && t.generic_alias_index_concrete_count == count {
|
||||
if t.generic_type_indexes_ready
|
||||
&& t.generic_alias_index_concrete_count == t.generic_checker_concrete_name_count() {
|
||||
return t.generic_concrete_type_names[name_id]
|
||||
}
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
if cache.checker_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
for name_source_id, _ in t.tc.structs { names[name_source_id] = true }
|
||||
for name_source_id, _ in t.tc.sum_types.ids { names[name_source_id] = true }
|
||||
for name_source_id, _ in t.tc.enum_names { names[name_source_id] = true }
|
||||
for name_source_id, _ in t.tc.interface_names { names[name_source_id] = true }
|
||||
cache.checker_names = names.move()
|
||||
cache.checker_count = count
|
||||
}
|
||||
return cache.checker_names[name_id]
|
||||
return name_id in t.tc.structs || name_id in t.tc.sum_types.ids || name_id in t.tc.enum_names
|
||||
|| name_id in t.tc.interface_names
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_arg_is_alias_name(arg string, module_name string) bool {
|
||||
@@ -10699,8 +10619,7 @@ fn (t &Transformer) qualify_generic_arg_for_decl_module(arg_id u32, module_name
|
||||
fn (t &Transformer) generic_main_arg_checker_declaration_known(name_id u32) bool {
|
||||
if t.generic_exact_alias_name_known(name_id) { return true }
|
||||
if isnil(t.tc) { return false }
|
||||
return t.refresh_enum_type_name_index().checker_enum_names[name_id]
|
||||
|| t.generic_checker_interface_name_known(name_id)
|
||||
return name_id in t.tc.enum_names || t.generic_checker_interface_name_known(name_id)
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_arg_module_owns_type(name string, module_name string) bool {
|
||||
@@ -16630,16 +16549,13 @@ fn (t &Transformer) generic_type_base_requires_args(typ_id u32) bool {
|
||||
if t.generic_type_indexes_ready && t.generic_parameter_type_index_count == count {
|
||||
return t.generic_parameter_type_names[clean_id] || t.generic_parameter_type_names[short_id]
|
||||
}
|
||||
mut cache := t.generic_type_name_index_cache
|
||||
if cache.parameter_count != count {
|
||||
mut names := map[u32]bool{}
|
||||
for name_source_id, _ in t.tc.struct_generic_params { names[name_source_id] = true }
|
||||
for name_source_id, _ in t.tc.sum_generic_params { names[name_source_id] = true }
|
||||
for name_source_id, _ in t.tc.type_alias_generic_params { names[name_source_id] = true }
|
||||
cache.parameter_names = names.move()
|
||||
cache.parameter_count = count
|
||||
}
|
||||
return cache.parameter_names[clean_id] || cache.parameter_names[short_id]
|
||||
return t.generic_parameter_type_name_known(clean_id)
|
||||
|| t.generic_parameter_type_name_known(short_id)
|
||||
}
|
||||
|
||||
fn (t &Transformer) generic_parameter_type_name_known(name_id u32) bool {
|
||||
return name_id in t.tc.struct_generic_params || name_id in t.tc.sum_generic_params
|
||||
|| name_id in t.tc.type_alias_generic_params
|
||||
}
|
||||
|
||||
fn strip_main_type_locks(typ string) string {
|
||||
@@ -17081,7 +16997,8 @@ fn (t &Transformer) is_known_main_struct_type_name_id(name_id u32) bool {
|
||||
&& t.generic_alias_index_authority_count == t.generic_type_authority_name_count() {
|
||||
return t.generic_main_struct_names[bare_id]
|
||||
}
|
||||
return t.refresh_transform_type_name_index().main_structs[bare_id]
|
||||
info := t.structs[bare_id] or { return false }
|
||||
return info.module in ['', 'main']
|
||||
}
|
||||
|
||||
fn (t &Transformer) type_name_is_declared(name string) bool {
|
||||
|
||||
@@ -1205,6 +1205,12 @@ mut:
|
||||
front_results [256]string
|
||||
front_states [256]i8
|
||||
front_generations [256]u32
|
||||
// Short source type id -> source type ids of the module-qualified,
|
||||
// non-generic structs with that short name. Rebuilt when the struct table
|
||||
// changes owner or size; template erasure resets bare_structs_len.
|
||||
bare_structs_tc voidptr
|
||||
bare_structs_len int = -1
|
||||
bare_structs map[u32][]u32
|
||||
}
|
||||
|
||||
// PromoteTextCache short-circuits repeated promote_scoped_result_text calls on
|
||||
|
||||
Reference in new issue
Block a user