puppet 5.0.1 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/lib/puppet/agent.rb +1 -0
  3. data/lib/puppet/defaults.rb +1 -1
  4. data/lib/puppet/functions.rb +6 -7
  5. data/lib/puppet/functions/all.rb +100 -0
  6. data/lib/puppet/functions/any.rb +105 -0
  7. data/lib/puppet/functions/defined.rb +3 -3
  8. data/lib/puppet/functions/new.rb +2 -1
  9. data/lib/puppet/functions/reduce.rb +31 -0
  10. data/lib/puppet/functions/tree_each.rb +200 -0
  11. data/lib/puppet/module.rb +30 -0
  12. data/lib/puppet/parser/functions/new.rb +67 -0
  13. data/lib/puppet/parser/functions/reduce.rb +31 -0
  14. data/lib/puppet/parser/relationship.rb +2 -2
  15. data/lib/puppet/parser/resource.rb +39 -10
  16. data/lib/puppet/parser/scope.rb +1 -1
  17. data/lib/puppet/pops/evaluator/access_operator.rb +11 -4
  18. data/lib/puppet/pops/evaluator/collector_transformer.rb +1 -1
  19. data/lib/puppet/pops/evaluator/evaluator_impl.rb +4 -4
  20. data/lib/puppet/pops/evaluator/relationship_operator.rb +1 -1
  21. data/lib/puppet/pops/evaluator/runtime3_converter.rb +3 -3
  22. data/lib/puppet/pops/evaluator/runtime3_resource_support.rb +1 -1
  23. data/lib/puppet/pops/loader/module_loaders.rb +1 -1
  24. data/lib/puppet/pops/loader/static_loader.rb +1 -2
  25. data/lib/puppet/pops/loaders.rb +1 -2
  26. data/lib/puppet/pops/lookup/context.rb +1 -1
  27. data/lib/puppet/pops/lookup/lookup_adapter.rb +2 -1
  28. data/lib/puppet/pops/model/ast.rb +440 -436
  29. data/lib/puppet/pops/model/factory.rb +140 -140
  30. data/lib/puppet/pops/pcore.rb +1 -2
  31. data/lib/puppet/pops/resource/param.rb +1 -1
  32. data/lib/puppet/pops/resource/resource_type_impl.rb +1 -1
  33. data/lib/puppet/pops/serialization/from_data_converter.rb +0 -12
  34. data/lib/puppet/pops/time/timestamp.rb +29 -17
  35. data/lib/puppet/pops/types/annotatable.rb +2 -2
  36. data/lib/puppet/pops/types/annotation.rb +8 -8
  37. data/lib/puppet/pops/types/class_loader.rb +3 -3
  38. data/lib/puppet/pops/types/implementation_registry.rb +1 -1
  39. data/lib/puppet/pops/types/iterable.rb +2 -2
  40. data/lib/puppet/pops/types/p_binary_type.rb +2 -2
  41. data/lib/puppet/pops/types/p_init_type.rb +238 -0
  42. data/lib/puppet/pops/types/p_meta_type.rb +14 -11
  43. data/lib/puppet/pops/types/p_object_type.rb +15 -15
  44. data/lib/puppet/pops/types/p_sem_ver_range_type.rb +2 -2
  45. data/lib/puppet/pops/types/p_sem_ver_type.rb +2 -2
  46. data/lib/puppet/pops/types/p_sensitive_type.rb +2 -2
  47. data/lib/puppet/pops/types/p_timespan_type.rb +6 -6
  48. data/lib/puppet/pops/types/p_timestamp_type.rb +6 -2
  49. data/lib/puppet/pops/types/p_type_set_type.rb +10 -9
  50. data/lib/puppet/pops/types/ruby_generator.rb +6 -5
  51. data/lib/puppet/pops/types/ruby_method.rb +2 -2
  52. data/lib/puppet/pops/types/string_converter.rb +1 -1
  53. data/lib/puppet/pops/types/tree_iterators.rb +250 -0
  54. data/lib/puppet/pops/types/type_calculator.rb +13 -13
  55. data/lib/puppet/pops/types/type_factory.rb +26 -7
  56. data/lib/puppet/pops/types/type_formatter.rb +9 -4
  57. data/lib/puppet/pops/types/type_parser.rb +8 -3
  58. data/lib/puppet/pops/types/type_set_reference.rb +2 -2
  59. data/lib/puppet/pops/types/types.rb +168 -109
  60. data/lib/puppet/provider/package/gem.rb +10 -9
  61. data/lib/puppet/provider/package/pip.rb +12 -3
  62. data/lib/puppet/provider/package/yum.rb +9 -1
  63. data/lib/puppet/resource/capability_finder.rb +30 -11
  64. data/lib/puppet/type/file.rb +21 -13
  65. data/lib/puppet/util/execution.rb +67 -14
  66. data/lib/puppet/util/suidmanager.rb +1 -0
  67. data/lib/puppet/version.rb +1 -1
  68. data/locales/puppet.pot +130 -66
  69. data/man/man5/puppet.conf.5 +1 -1
  70. data/spec/fixtures/unit/provider/package/yum/yum-check-update-simple.txt +1 -0
  71. data/spec/integration/parser/collection_spec.rb +40 -1
  72. data/spec/shared_contexts/types_setup.rb +41 -2
  73. data/spec/unit/agent_spec.rb +11 -0
  74. data/spec/unit/file_bucket/dipper_spec.rb +13 -4
  75. data/spec/unit/functions/all_spec.rb +97 -0
  76. data/spec/unit/functions/any_spec.rb +109 -0
  77. data/spec/unit/functions/hiera_spec.rb +5 -0
  78. data/spec/unit/functions/new_spec.rb +66 -0
  79. data/spec/unit/functions/tree_each_spec.rb +444 -0
  80. data/spec/unit/module_spec.rb +29 -0
  81. data/spec/unit/pops/serialization/serialization_spec.rb +2 -2
  82. data/spec/unit/pops/serialization/to_from_hr_spec.rb +2 -2
  83. data/spec/unit/pops/types/iterable_spec.rb +9 -9
  84. data/spec/unit/pops/types/p_init_type_spec.rb +285 -0
  85. data/spec/unit/pops/types/p_object_type_spec.rb +8 -8
  86. data/spec/unit/pops/types/p_sensitive_type_spec.rb +4 -0
  87. data/spec/unit/pops/types/p_timespan_type_spec.rb +14 -0
  88. data/spec/unit/pops/types/p_timestamp_type_spec.rb +19 -1
  89. data/spec/unit/pops/types/p_type_set_type_spec.rb +2 -2
  90. data/spec/unit/pops/types/ruby_generator_spec.rb +9 -22
  91. data/spec/unit/pops/types/string_converter_spec.rb +2 -2
  92. data/spec/unit/pops/types/type_acceptor_spec.rb +2 -2
  93. data/spec/unit/pops/types/type_calculator_spec.rb +43 -38
  94. data/spec/unit/pops/types/type_factory_spec.rb +6 -6
  95. data/spec/unit/pops/types/type_formatter_spec.rb +6 -6
  96. data/spec/unit/pops/types/types_spec.rb +16 -4
  97. data/spec/unit/provider/package/gem_spec.rb +6 -6
  98. data/spec/unit/provider/package/pip_spec.rb +44 -23
  99. data/spec/unit/provider/package/puppet_gem_spec.rb +1 -1
  100. data/spec/unit/provider/package/yum_spec.rb +8 -0
  101. data/spec/unit/resource/capability_finder_spec.rb +4 -5
  102. data/spec/unit/type/file_spec.rb +19 -14
  103. data/spec/unit/util/execution_spec.rb +216 -82
  104. data/tasks/generate_ast_model.rake +10 -2
  105. metadata +15 -2
@@ -96,8 +96,7 @@ module Pcore
96
96
  aliases.each do |name, type_string|
97
97
  add_type(Types::PTypeAliasType.new(name, Types::TypeFactory.type_reference(type_string), nil), loader, name_authority)
98
98
  end
99
- parser = Types::TypeParser.singleton
100
- aliases.each_key.map { |name| loader.load(:type, name).resolve(parser, loader) }
99
+ aliases.each_key.map { |name| loader.load(:type, name).resolve(loader) }
101
100
  end
102
101
  end
103
102
  end
@@ -21,7 +21,7 @@ class Param
21
21
  def self.register_ptype(loader, ir)
22
22
  @ptype = Pcore::create_object_type(loader, ir, self, 'Puppet::Resource::Param', nil,
23
23
  {
24
- Types::KEY_TYPE => Types::PType::DEFAULT,
24
+ Types::KEY_TYPE => Types::PTypeType::DEFAULT,
25
25
  Types::KEY_NAME => Types::PStringType::NON_EMPTY,
26
26
  'name_var' => {
27
27
  Types::KEY_TYPE => Types::PBooleanType::DEFAULT,
@@ -7,7 +7,7 @@ def self.register_ptypes(loader, ir)
7
7
  types = [Param, ResourceTypeImpl].map do |c|
8
8
  c.register_ptype(loader, ir)
9
9
  end
10
- types.each {|t| t.resolve(Types::TypeParser.singleton, loader) }
10
+ types.each {|t| t.resolve(loader) }
11
11
  end
12
12
 
13
13
  class ResourceTypeImpl
@@ -164,18 +164,6 @@ module Serialization
164
164
  { :type_name => pcore_type.name, :arg_class => value.class.name }
165
165
  end
166
166
  end
167
-
168
- def data_to_pcore_type(pcore_type)
169
- if pcore_type.is_a?(Hash)
170
- without_value { convert(pcore_type) }
171
- else
172
- type = Types::TypeParser.singleton.parse(pcore_type, @loader)
173
- if type.is_a?(Types::PTypeReferenceType)
174
- raise SerializationError, _('No implementation mapping found for Puppet Type %{type_name}') % { type_name: pcore_type }
175
- end
176
- type
177
- end
178
- end
179
167
  end
180
168
  end
181
169
  end
@@ -71,31 +71,43 @@ class Timestamp < TimeData
71
71
  parsed = nil
72
72
  if format.is_a?(Array)
73
73
  format.each do |fmt|
74
- assert_no_tz_extractor(fmt) if has_timezone
75
- begin
76
- parsed = DateTime.strptime(str, fmt)
77
- break
78
- rescue ArgumentError
74
+ parsed = DateTime._strptime(str, fmt)
75
+ next if parsed.nil?
76
+ if parsed.include?(:leftover) || (has_timezone && parsed.include?(:zone))
77
+ parsed = nil
78
+ next
79
79
  end
80
+ break
81
+ end
82
+ if parsed.nil?
83
+ raise ArgumentError, _(
84
+ "Unable to parse '%{str}' using any of the formats %{formats}") % { str: str, formats: format.join(', ') }
80
85
  end
81
- raise ArgumentError, _("Unable to parse '%{str}' using any of the formats %{formats}") % { str: str, formats: format.join(', ') } if parsed.nil?
82
86
  else
83
- assert_no_tz_extractor(format) if has_timezone
84
- begin
85
- parsed = DateTime.strptime(str, format)
86
- rescue ArgumentError
87
+ parsed = DateTime._strptime(str, format)
88
+ if parsed.nil? || parsed.include?(:leftover)
87
89
  raise ArgumentError, _("Unable to parse '%{str}' using format '%{format}'") % { str: str, format: format }
88
90
  end
91
+ if has_timezone && parsed.include?(:zone)
92
+ raise ArgumentError, _(
93
+ 'Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument')
94
+ end
95
+ end
96
+ unless has_timezone
97
+ timezone = parsed[:zone]
98
+ has_timezone = !timezone.nil?
89
99
  end
90
- parsed_time = parsed.to_time
100
+ fraction = parsed[:sec_fraction]
101
+
102
+ # Convert msec rational found in _strptime hash to usec
103
+ fraction = fraction * 1000000 unless fraction.nil?
104
+
105
+ # Create the Time instance and adjust for timezone
106
+ parsed_time = ::Time.utc(parsed[:year], parsed[:mon], parsed[:mday], parsed[:hour], parsed[:min], parsed[:sec], fraction)
91
107
  parsed_time -= utc_offset(timezone) if has_timezone
92
- from_time(parsed_time)
93
- end
94
108
 
95
- def self.assert_no_tz_extractor(format)
96
- if format =~ /[^%]%[zZ]/
97
- raise ArgumentError, _('Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument')
98
- end
109
+ # Convert to Timestamp
110
+ from_time(parsed_time)
99
111
  end
100
112
 
101
113
  undef_method :-@, :+@, :div, :fdiv, :abs, :abs2, :magnitude # does not make sense on a Timestamp
@@ -7,9 +7,9 @@ KEY_ANNOTATIONS = 'annotations'.freeze
7
7
  #
8
8
  # @api public
9
9
  module Annotatable
10
- TYPE_ANNOTATIONS = PHashType.new(PType.new(PTypeReferenceType.new('Annotation')), PHashType::DEFAULT)
10
+ TYPE_ANNOTATIONS = PHashType.new(PTypeType.new(PTypeReferenceType.new('Annotation')), PHashType::DEFAULT)
11
11
 
12
- # @return [{PType => PStructType}] the map of annotations
12
+ # @return [{PTypeType => PStructType}] the map of annotations
13
13
  # @api public
14
14
  def annotations
15
15
  @annotations.nil? ? EMPTY_HASH : @annotations
@@ -21,29 +21,29 @@ module Types
21
21
  # If no annotation was found and no block is given, this method returns `nil`
22
22
  #
23
23
  # @param o [Object] object to annotate
24
- # @param block [Proc] optional, evaluated when a new annotation must be created. Should return the i12n hash
24
+ # @param block [Proc] optional, evaluated when a new annotation must be created. Should return the init hash
25
25
  # @return [Annotation<self>] an annotation of the same class as the receiver of the call
26
26
  #
27
27
  def self.annotate(o)
28
28
  adapter = get(o)
29
29
  if adapter.nil?
30
30
  if o.is_a?(Annotatable)
31
- i12n = o.annotations[_pcore_type]
32
- i12n = yield if i12n.nil? && block_given?
31
+ init_hash = o.annotations[_pcore_type]
32
+ init_hash = yield if init_hash.nil? && block_given?
33
33
  else
34
- i12n = yield if block_given?
34
+ init_hash = yield if block_given?
35
35
  end
36
- adapter = associate_adapter(_pcore_type.from_hash(i12n), o) unless i12n.nil?
36
+ adapter = associate_adapter(_pcore_type.from_hash(init_hash), o) unless init_hash.nil?
37
37
  end
38
38
  adapter
39
39
  end
40
40
 
41
41
  # Forces the creation or removal of an annotation of this type.
42
- # If `i21n` is a hash, a new annotation is created and returned
43
- # If `i12n` is `nil`, then the annotation is cleared and the previous annotation is returned.
42
+ # If `init_hash` is a hash, a new annotation is created and returned
43
+ # If `init_hash` is `nil`, then the annotation is cleared and the previous annotation is returned.
44
44
  #
45
45
  # @param o [Object] object to annotate
46
- # @param i12n [Hash{String,Object},nil] the initializer for the annotation or `nil` to clear the annotation
46
+ # @param init_hash [Hash{String,Object},nil] the initializer for the annotation or `nil` to clear the annotation
47
47
  # @return [Annotation<self>] an annotation of the same class as the receiver of the call
48
48
  #
49
49
  def self.annotate_new(o, init_hash)
@@ -24,7 +24,7 @@ class ClassLoader
24
24
  when Array
25
25
  provide_from_name_path(name.join('::'), name)
26
26
 
27
- when PAnyType, PType
27
+ when PAnyType, PTypeType
28
28
  provide_from_type(name)
29
29
 
30
30
  else
@@ -44,8 +44,8 @@ class ClassLoader
44
44
  # There is no other thing to load except this Enum meta type
45
45
  RGen::MetamodelBuilder::MMBase::Boolean
46
46
 
47
- when PType
48
- # TODO: PType should has a type argument (a PAnyType) so the Class' class could be returned
47
+ when PTypeType
48
+ # TODO: PTypeType should have a type argument (a PAnyType) so the Class' class could be returned
49
49
  # (but this only matters in special circumstances when meta programming has been used).
50
50
  Class
51
51
 
@@ -41,7 +41,7 @@ module Types
41
41
  TypeAsserter.assert_instance_of('Second argument of type mapping', TYPE_REGEXP_SUBST, puppet_type_or_pattern)
42
42
  register_implementation_regexp(puppet_type_or_pattern, expr, loader)
43
43
  else
44
- TypeAsserter.assert_instance_of('Second argument of type mapping', PType::DEFAULT, puppet_type_or_pattern)
44
+ TypeAsserter.assert_instance_of('Second argument of type mapping', PTypeType::DEFAULT, puppet_type_or_pattern)
45
45
  register_implementation(puppet_type_or_pattern, expr, loader)
46
46
  end
47
47
  end
@@ -132,13 +132,13 @@ module Puppet::Pops::Types
132
132
  def reverse_each(&block)
133
133
  # Default implementation cannot propagate reverse_each to a new enumerator so chained
134
134
  # calls must put reverse_each last.
135
- raise ArgumentError 'reverse_each() is not implemented'
135
+ raise ArgumentError, 'reverse_each() is not implemented'
136
136
  end
137
137
 
138
138
  def step(step, &block)
139
139
  # Default implementation cannot propagate step to a new enumerator so chained
140
140
  # calls must put stepping last.
141
- raise ArgumentError 'step() is not implemented'
141
+ raise ArgumentError, 'step() is not implemented'
142
142
  end
143
143
 
144
144
  def to_a
@@ -144,8 +144,8 @@ class PBinaryType < PAnyType
144
144
  end
145
145
 
146
146
  # @api private
147
- def self.new_function(_, loader)
148
- @new_function ||= Puppet::Functions.create_loaded_function(:new_Binary, loader) do
147
+ def self.new_function(type)
148
+ @new_function ||= Puppet::Functions.create_loaded_function(:new_Binary, type.loader) do
149
149
  local_types do
150
150
  type 'ByteInteger = Integer[0,255]'
151
151
  type 'Base64Format = Enum["%b", "%u", "%B", "%s", "%r"]'
@@ -0,0 +1,238 @@
1
+ module Puppet::Pops
2
+ module Types
3
+
4
+ # @api public
5
+ class PInitType < PTypeWithContainedType
6
+ def self.register_ptype(loader, ir)
7
+ create_ptype(loader, ir, 'AnyType',
8
+ 'type' => {
9
+ KEY_TYPE => POptionalType.new(PTypeType::DEFAULT),
10
+ KEY_VALUE => nil
11
+ },
12
+ 'init_args' => {
13
+ KEY_TYPE => PArrayType::DEFAULT,
14
+ KEY_VALUE => EMPTY_ARRAY
15
+ }
16
+ )
17
+ end
18
+
19
+ attr_reader :init_args
20
+
21
+ def initialize(type, init_args)
22
+ super(type)
23
+ @init_args = init_args.nil? ? EMPTY_ARRAY : init_args
24
+
25
+ if type.nil?
26
+ raise ArgumentError, _('Init cannot be parameterized with an undefined type and additional arguments') unless @init_args.empty?
27
+ @initialized = true
28
+ else
29
+ @initialized = false
30
+ end
31
+ end
32
+
33
+ def instance?(o, guard = nil)
34
+ really_instance?(o, guard) == 1
35
+ end
36
+
37
+ # @api private
38
+ def really_instance?(o, guard = nil)
39
+ if @type.nil?
40
+ TypeFactory.rich_data.really_instance?(o)
41
+ else
42
+ assert_initialized
43
+ guarded_recursion(guard, 0) do |g|
44
+ v = @type.really_instance?(o, g)
45
+ if v < 1
46
+ if @single_type
47
+ s = @single_type.really_instance?(o, g)
48
+ v = s if s > v
49
+ end
50
+ end
51
+ if v < 1
52
+ if @other_type
53
+ s = @other_type.really_instance?(o, g)
54
+ s = @other_type.really_instance?([o], g) if s < 0 && @has_optional_single
55
+ v = s if s > v
56
+ end
57
+ end
58
+ v
59
+ end
60
+ end
61
+ end
62
+
63
+ def eql?(o)
64
+ super && @init_args == o.init_args
65
+ end
66
+
67
+ def hash
68
+ super ^ @init_args.hash
69
+ end
70
+
71
+ def new_function
72
+ return super if type.nil?
73
+ assert_initialized
74
+
75
+ target_type = type
76
+ single_type = @single_type
77
+ if @init_args.empty?
78
+ @new_function ||= Puppet::Functions.create_function(:new_Init, Puppet::Functions::InternalFunction) do
79
+ @target_type = target_type
80
+ @single_type = single_type
81
+
82
+ dispatch :from_array do
83
+ scope_param
84
+ param 'Array', :value
85
+ end
86
+
87
+ dispatch :create do
88
+ scope_param
89
+ param 'Any', :value
90
+ end
91
+
92
+ def self.create(scope, value, func)
93
+ func.call(scope, @target_type, value)
94
+ end
95
+
96
+ def self.from_array(scope, value, func)
97
+ # If there is a single argument that matches the array, then that gets priority over
98
+ # expanding the array into all arguments
99
+ if @single_type.instance?(value) || (@other_type && !@other_type.instance?(value) && @has_optional_single && @other_type.instance?([value]))
100
+ func.call(scope, @target_type, value)
101
+ else
102
+ func.call(scope, @target_type, *value)
103
+ end
104
+ end
105
+
106
+ def from_array(scope, value)
107
+ self.class.from_array(scope, value, loader.load(:function, 'new'))
108
+ end
109
+
110
+ def create(scope, value)
111
+ self.class.create(scope, value, loader.load(:function, 'new'))
112
+ end
113
+ end
114
+ else
115
+ init_args = @init_args
116
+ @new_function ||= Puppet::Functions.create_function(:new_Init, Puppet::Functions::InternalFunction) do
117
+ @target_type = target_type
118
+ @init_args = init_args
119
+
120
+ dispatch :create do
121
+ scope_param
122
+ param 'Any', :value
123
+ end
124
+
125
+ def self.create(scope, value, func)
126
+ func.call(scope, @target_type, value, *@init_args)
127
+ end
128
+
129
+ def create(scope, value)
130
+ self.class.create(scope, value, loader.load(:function, 'new'))
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ DEFAULT = PInitType.new(nil, EMPTY_ARRAY)
137
+
138
+ EXACTLY_ONE = [1, 1].freeze
139
+
140
+ def assert_initialized
141
+ return self if @initialized
142
+
143
+ @initialized = true
144
+ @self_recursion = true
145
+
146
+ begin
147
+ # Filter out types that will provide a new_function but are unsuitable to be contained in Init
148
+ #
149
+ # Calling Init#new would cause endless recursion
150
+ # The Optional is the same as Variant[T,Undef].
151
+ # The NotUndef is not meaningful to create instances of
152
+ if @type.instance_of?(PInitType) || @type.instance_of?(POptionalType) || @type.instance_of?(PNotUndefType)
153
+ raise ArgumentError.new
154
+ end
155
+ new_func = @type.new_function
156
+ rescue ArgumentError
157
+ raise ArgumentError, _("Creation of new instance of type '%{type_name}' is not supported") % { type_name: @type.to_s }
158
+ end
159
+ param_tuples = new_func.dispatcher.dispatchers.map { |closure| closure.type.param_types }
160
+
161
+ # An instance of the contained type is always a match to this type.
162
+ single_types = [@type]
163
+
164
+ if @init_args.empty?
165
+ # A value that is assignable to the type of a single parameter is also a match
166
+ single_tuples, other_tuples = param_tuples.partition { |tuple| EXACTLY_ONE == tuple.size_range }
167
+ single_types.concat(single_tuples.map { |tuple| tuple.types[0] })
168
+ else
169
+ tc = TypeCalculator.singleton
170
+ init_arg_types = @init_args.map { |arg| tc.infer_set(arg) }
171
+ arg_count = 1 + init_arg_types.size
172
+
173
+ # disqualify all parameter tuples that doesn't allow one value (type unknown at ths stage) + init args.
174
+ param_tuples = param_tuples.select do |tuple|
175
+ min, max = tuple.size_range
176
+ if arg_count >= min && arg_count <= max
177
+ # Aside from the first parameter, does the other parameters match?
178
+ tuple.assignable?(PTupleType.new(tuple.types[0..0].concat(init_arg_types)))
179
+ else
180
+ false
181
+ end
182
+ end
183
+ if param_tuples.empty?
184
+ raise ArgumentError, _("The type '%{type}' does not represent a valid set of parameters for %{subject}.new()") %
185
+ { type: to_s, subject: @type.generalize.name }
186
+ end
187
+ single_types.concat(param_tuples.map { |tuple| tuple.types[0] })
188
+ other_tuples = EMPTY_ARRAY
189
+ end
190
+ @single_type = PVariantType.maybe_create(single_types)
191
+ unless other_tuples.empty?
192
+ @other_type = PVariantType.maybe_create(other_tuples)
193
+ @has_optional_single = other_tuples.any? { |tuple| tuple.size_range.min == 1 }
194
+ end
195
+
196
+ guard = RecursionGuard.new
197
+ accept(NoopTypeAcceptor::INSTANCE, guard)
198
+ @self_recursion = guard.recursive_this?(self)
199
+ end
200
+
201
+ def accept(visitor, guard)
202
+ guarded_recursion(guard, nil) do |g|
203
+ super(visitor, g)
204
+ @single_type.accept(visitor, guard) if @single_type
205
+ @other_type.accept(visitor, guard) if @other_type
206
+ end
207
+ end
208
+
209
+ protected
210
+
211
+ def _assignable?(o, guard)
212
+ guarded_recursion(guard, false) do |g|
213
+ assert_initialized
214
+ if o.is_a?(PInitType)
215
+ @type.nil? || @type.assignable?(o.type, g)
216
+ elsif @type.nil?
217
+ TypeFactory.rich_data.assignable?(o, g)
218
+ else
219
+ @type.assignable?(o, g) ||
220
+ @single_type && @single_type.assignable?(o, g) ||
221
+ @other_type && (@other_type.assignable?(o, g) || @has_optional_single && @other_type.assignable?(PTupleType.new([o])))
222
+ end
223
+ end
224
+ end
225
+
226
+ private
227
+
228
+ def guarded_recursion(guard, dflt)
229
+ if @self_recursion
230
+ guard ||= RecursionGuard.new
231
+ guard.with_this(self) { |state| (state & RecursionGuard::SELF_RECURSION_IN_THIS) == 0 ? yield(guard) : dflt }
232
+ else
233
+ yield(guard)
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end