8000 Stack overflow when specializing generic type instances · Issue #851 · inko-lang/inko · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Stack overflow when specializing generic type instances #851
Closed
@yorickpeterse

Description

@yorickpeterse

Please describe the bug

While refactoring std.optparse I ran into the following stack overflow:

[...]
#364 0x00005555591f190f in types::specialize::TypeSpecializer::specialize_generic_instance (self=..., ins=...) at specialize.rs:338
#365 0x00005555591f1221 in types::specialize::TypeSpecializer::specialize_type_instance (self=..., ins=...) at specialize.rs:263
#366 0x00005555591f10d4 in types::specialize::TypeSpecializer::specialize (self=..., value=...) at specialize.rs:148
#367 0x00005555591ff982 in types::specialize::{impl#0}::specialize_generic_instance::{closure#0} (p=...) at specialize.rs:343
#368 0x000055555920eb85 in core::iter::adapters::map::map_fold::{closure#0}<types::TypeParameterId, types::Shape, (), types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}, cor
e::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<types::Shape, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<types::Shape, alloc::alloc::Global, core::iter::adapters::
map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>>>> (acc=..., elt=...)
    at map.rs:89
#369 0x00005555591ede3e in core::iter::traits::iterator::Iterator::fold<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, (), core::iter::adapters::map::map_fold::{clo
sure_env#0}<types::TypeParameterId, types::Shape, (), types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}, core::iter::traits::iterator::Iterator::for_each::call::{closure_en
v#0}<types::Shape, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<types::Shape, alloc::alloc::Global, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId,
 alloc::alloc::Global>, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>>>>> (self=..., init=..., f=...) at iterator.rs:2587
#370 0x000055555920e08d in core::iter::adapters::map::{impl#2}::fold<types::Shape, alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::specialize::{impl#0}::speci
alize_generic_instance::{closure_env#0}, (), core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<types::Shape, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<types::Sha
pe, alloc::alloc::Global, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::specialize::{impl#0}::specialize_generic_instance::{c
losure_env#0}>>>> (self=..., init=..., g=...) at map.rs:129
#371 0x000055555920e7a6 in core::iter::traits::iterator::Iterator::for_each<core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::spe
cialize::{impl#0}::specialize_generic_instance::{closure_env#0}>, alloc::vec::{impl#19}::extend_trusted::{closure_env#0}<types::Shape, alloc::alloc::Global, core::iter::adapters::map::Map<alloc::v
ec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>>> (self=..., f=...) at iterator.rs:817
#372 0x0000555559239467 in alloc::vec::Vec<types::Shape, alloc::alloc::Global>::extend_trusted<types::Shape, alloc::alloc::Global, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<ty
pes::TypeParameterId, alloc::alloc::Global>, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>> (self=..., iterator=...) at mod.rs:3020
#373 0x000055555924376b in alloc::vec::spec_extend::{impl#1}::spec_extend<types::Shape, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>
, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>, alloc::alloc::Global> (self=..., iterator=...) at spec_extend.rs:26
#374 0x0000555559238395 in alloc::vec::spec_from_iter_nested::{impl#1}::from_iter<types::Shape, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc:
:Global>, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>> (iterator=...) at spec_from_iter_nested.rs:62
#375 0x000055555923eb92 in alloc::vec::in_place_collect::{impl#1}::from_iter<types::Shape, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Glob
al>, types::specialize::{impl#0}::specialize_generic_instance::{closure_env#0}>> (iterator=...) at in_place_collect.rs:237
#376 0x0000555559243529 in alloc::vec::{impl#14}::from_iter<types::Shape, core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::speci
alize::{impl#0}::specialize_generic_instance::{closure_env#0}>> (iter=...) at mod.rs:2894
#377 0x000055555920e6be in core::iter::traits::iterator::Iterator::collect<core::iter::adapters::map::Map<alloc::vec::into_iter::IntoIter<types::TypeParameterId, alloc::alloc::Global>, types::spec
ialize::{impl#0}::specialize_generic_instance::{closure_env#0}>, alloc::vec::Vec<types::Shape, alloc::alloc::Global>> (self=...) at iterator.rs:2003
#378 0x00005555591f190f in types::specialize::TypeSpecializer::specialize_generic_instance (self=..., ins=...) at specialize.rs:338
#379 0x00005555591f1221 in types::specialize::TypeSpecializer::specialize_type_instance (self=..., ins=...) at specialize.rs:263
#380 0x00005555591f10d4 in types::specialize::TypeSpecializer::specialize (self=..., value=...) at specialize.rs:148
#381 0x00005555591ff982 in types::specialize::{impl#0}::specialize_generic_instance::{closure#0} (p=...) at specialize.rs:343
[...]

The input file is as follows:

import std.env
import std.fmt (fmt)
import std.optparse (Options)
import std.stdio (Stderr, Stdout)
import std.sys

type async Main {
  fn async main {
    let opts = Options.new('example')

    opts.description = 'This is an example program to showcase the optparse library.'

    opts.flag('h', 'help', 'Show this help message')
    opts.single('c', 'config', 'PATH', 'Use a custom configuration file')
    opts.multiple('i', 'include', 'DIR', 'Add the directory')
    opts.multiple('I', 'ignore', '', 'Ignore something')
    opts.flag('', 'verbose', 'Use verbose output,\nlorem ipsum')
    opts.flag('', 'example', '')
    opts.flag('x', '', 'Foo')

    let matches = match opts.parse(env.arguments) {
      case Ok(v) -> v
      case Error(e) -> {
        let _ = Stderr.new.print('test: ${e}')

        sys.exit(1)
      }
    }

    if matches.contains?('help') {
      Stdout.new.write(opts.to_string)
    } else {
      Stdout.new.print(fmt(matches.remaining))
    }
  }
}

The standard library state is as follows:

diff --git a/std/src/std/optparse.inko b/std/src/std/optparse.inko
index 77f90add..9bb4ac42 100644
--- a/std/src/std/optparse.inko
+++ b/std/src/std/optparse.inko
@@ -35,7 +35,7 @@
 #
 # type async Main {
 #   fn async main {
-#   
8000
  let opts = Options.new
+#     let opts = Options.new('example')
 #
 #     opts.flag('h', 'help', 'Show this help message')
 #
@@ -64,7 +64,7 @@
 #
 # type async Main {
 #   fn async main {
-#     let opts = Options.new
+#     let opts = Options.new('example')
 #
 #     opts.flag('h', 'help', 'Show this help message')
 #     opts.stop_at_first_non_option = true
@@ -84,18 +84,20 @@
 #
 # # Help messages
 #
-# Help messages are generated using the `Help` type:
+# Help messages are generated using `Options.to_string`:
 #
 # ```inko
 # import std.env
 # import std.fmt (fmt)
-# import std.optparse (Help, Options)
+# import std.optparse (Options)
 # import std.stdio (Stderr, Stdout)
 # import std.sys
 #
 # type async Main {
 #   fn async main {
-#     let opts = Options.new
+#     let opts = Options.new('example')
+#
+#     opts.description = 'This is an example program to showcase the optparse library.'
 #
 #     opts.flag('h', 'help', 'Show this help message')
 #     opts.single('c', 'config', 'PATH', 'Use a custom configuration file')
@@ -108,24 +110,14 @@
 #     let matches = match opts.parse(env.arguments) {
 #       case Ok(v) -> v
 #       case Error(e) -> {
-#         Stderr.new.print('test: ${e}')
+#         let _ = Stderr.new.print('test: ${e}')
+#
 #         sys.exit(1)
 #       }
 #     }
 #
 #     if matches.contains?('help') {
-#       let help = Help
-#         .new('test')
-#         .description(
-#           'This is an example program to showcase the optparse library.',
-#         )
-#         .section('Examples')
-#         .line('test --help')
-#         .section('Options')
-#         .options(opts)
-#         .to_string
-#
-#       Stdout.new.write(help)
+#       Stdout.new.write(opts.to_string)
 #     } else {
 #       Stdout.new.print(fmt(matches.remaining))
 #     }
@@ -136,14 +128,10 @@
 # When running this program with the `--help` option, the output is as follows:
 #
 # ```
-# Usage: test [OPTIONS]
+# Usage: example [OPTIONS]
 #
 # This is an example program to showcase the optparse library.
 #
-# Examples:
-#
-#   test --help
-#
 # Options:
 #
 #   -h, --help           Show this help message
@@ -537,8 +525,19 @@ type Opt {
   let @hint: String
 }
 
-# A type that describes the options to parse.
+# A type for defining and parsing command-line options and arguments.
 type pub Options {
+  # The name of the program.
+  let pub @name: String
+
+  # The usage section of the help message.
+  #
+  # This field defaults to the value `'[OPTIONS]'`.
+  let pub mut @usage: String
+
+  # The description of the command.
+  let pub mut @description: String
+
   # All options that have been defined.
   let @options: Array[Opt]
 
@@ -553,9 +552,18 @@ type pub Options {
   # definitions.
   let @mapping: Map[String, ref Opt]
 
-  # Returns a new empty `Options`.
-  fn pub static new -> Options {
-    Options(mapping: Map.new, options: [], stop_at_first_non_option: false)
+  # Returns a new `Options` value.
+  #
+  # The `name` argument is the name of the program.
+  fn pub static new(name: String) -> Options {
+    Options(
+      name: name,
+      usage: '[OPTIONS]',
+      description: '',
+      mapping: Map.new,
+      options: [],
+      stop_at_first_non_option: false,
+    )
   }
 
   # Adds a boolean option that can be specified once.
@@ -836,14 +844,19 @@ impl ToString for Options {
       if chars > max { chars } else { max }
     })
 
-    let buf = StringBuffer.new
+    let buf = StringBuffer.from_array(['Usage: ', @name, ' ', @usage, '\n'])
 
-    for (idx, opt) in opts.into_iter.with_index {
-      let desc = descs.get(idx).or_panic
+    if @description.size > 0 {
+      buf.push('\n')
+      buf.push(@description)
+      buf.push('\n')
+    }
+
+    buf.push('\nOptions:\n\n')
+
+    for (opt, desc) in opts.into_iter.zip(descs.into_iter) {
       let has_desc = desc.size > 0
 
-      if idx > 0 { buf.push('\n') }
-
       buf.push(INDENT)
 
       if has_desc {
@@ -860,6 +873,8 @@ impl ToString for Options {
       } else {
         buf.push(opt)
       }
+
+      buf.push('\n')
     }
 
     buf.into_string

Operating system

Fedora

Inko version

main

Metadata

Metadata

Assignees

Labels

accepting contributionsIssues that are suitable to be worked on by anybody, not just maintainersbugDefects, unintended behaviour, etccompilerChanges related to the compiler

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions

    0