Closed
Description
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