Method: Puppet::Pops::Types::TypeCalculator#common_type
- Defined in:
- lib/puppet/pops/types/type_calculator.rb
#common_type(t1, t2) ⇒ Object
Answers, ‘What is the common type of t1 and t2?’
TODO: The current implementation should be optimized for performance
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
# File 'lib/puppet/pops/types/type_calculator.rb', line 330 def common_type(t1, t2) raise ArgumentError, 'two types expected' unless (is_ptype?(t1) || is_pnil?(t1)) && (is_ptype?(t2) || is_pnil?(t2)) # TODO: This is not right since Scalar U Undef is Any # if either is nil, the common type is the other if is_pnil?(t1) return t2 elsif is_pnil?(t2) return t1 end # If either side is Unit, it is the other type if t1.is_a?(PUnitType) return t2 elsif t2.is_a?(PUnitType) return t1 end # Simple case, one is assignable to the other if assignable?(t1, t2) return t1 elsif assignable?(t2, t1) return t2 end # when both are arrays, return an array with common element type if t1.is_a?(PArrayType) && t2.is_a?(PArrayType) return PArrayType.new(common_type(t1.element_type, t2.element_type)) end # when both are hashes, return a hash with common key- and element type if t1.is_a?(PHashType) && t2.is_a?(PHashType) key_type = common_type(t1.key_type, t2.key_type) value_type = common_type(t1.value_type, t2.value_type) return PHashType.new(key_type, value_type) end # when both are host-classes, reduce to PHostClass[] (since one was not assignable to the other) if t1.is_a?(PClassType) && t2.is_a?(PClassType) return PClassType::DEFAULT end # when both are resources, reduce to Resource[T] or Resource[] (since one was not assignable to the other) if t1.is_a?(PResourceType) && t2.is_a?(PResourceType) # only Resource[] unless the type name is the same return t1.type_name == t2.type_name ? PResourceType.new(t1.type_name, nil) : PResourceType::DEFAULT end # Integers have range, expand the range to the common range if t1.is_a?(PIntegerType) && t2.is_a?(PIntegerType) return PIntegerType.new([t1.numeric_from, t2.numeric_from].min, [t1.numeric_to, t2.numeric_to].max) end # Floats have range, expand the range to the common range if t1.is_a?(PFloatType) && t2.is_a?(PFloatType) return PFloatType.new([t1.numeric_from, t2.numeric_from].min, [t1.numeric_to, t2.numeric_to].max) end if t1.is_a?(PStringType) && (t2.is_a?(PStringType) || t2.is_a?(PEnumType)) if t2.is_a?(PEnumType) return t1.value.nil? ? PEnumType::DEFAULT : PEnumType.new(t2.values | [t1.value]) end if t1.size_type.nil? || t2.size_type.nil? return t1.value.nil? || t2.value.nil? ? PStringType::DEFAULT : PEnumType.new([t1.value, t2.value]) end return PStringType.new(common_type(t1.size_type, t2.size_type)) end if t1.is_a?(PPatternType) && t2.is_a?(PPatternType) return PPatternType.new(t1.patterns | t2.patterns) end if t1.is_a?(PEnumType) && (t2.is_a?(PStringType) || t2.is_a?(PEnumType)) # The common type is one that complies with either set if t2.is_a?(PEnumType) return PEnumType.new(t1.values | t2.values) end return t2.value.nil? ? PEnumType::DEFAULT : PEnumType.new(t1.values | [t2.value]) end if t1.is_a?(PVariantType) && t2.is_a?(PVariantType) # The common type is one that complies with either set return PVariantType.maybe_create(t1.types | t2.types) end if t1.is_a?(PRegexpType) && t2.is_a?(PRegexpType) # if they were identical, the general rule would return a parameterized regexp # since they were not, the result is a generic regexp type return PRegexpType::DEFAULT end if t1.is_a?(PCallableType) && t2.is_a?(PCallableType) # They do not have the same signature, and one is not assignable to the other, # what remains is the most general form of Callable return PCallableType::DEFAULT end # Common abstract types, from most specific to most general if common_numeric?(t1, t2) return PNumericType::DEFAULT end if common_scalar_data?(t1, t2) return PScalarDataType::DEFAULT end if common_scalar?(t1, t2) return PScalarType::DEFAULT end if common_data?(t1, t2) return TypeFactory.data end # Meta types Type[Integer] + Type[String] => Type[Data] if t1.is_a?(PTypeType) && t2.is_a?(PTypeType) return PTypeType.new(common_type(t1.type, t2.type)) end if common_rich_data?(t1, t2) return TypeFactory.rich_data end # If both are Runtime types if t1.is_a?(PRuntimeType) && t2.is_a?(PRuntimeType) if t1.runtime == t2.runtime && t1.runtime_type_name == t2.runtime_type_name return t1 end # finding the common super class requires that names are resolved to class # NOTE: This only supports runtime type of :ruby c1 = ClassLoader.provide_from_type(t1) c2 = ClassLoader.provide_from_type(t2) if c1 && c2 c2_superclasses = superclasses(c2) superclasses(c1).each do |c1_super| c2_superclasses.each do |c2_super| if c1_super == c2_super return PRuntimeType.new(:ruby, c1_super.name) end end end end end # They better both be Any type, or the wrong thing was asked and nil is returned t1.is_a?(PAnyType) && t2.is_a?(PAnyType) ? PAnyType::DEFAULT : nil end |