A specific analysis that detects the variance constraints of formal parameters.

The client has 3 steps to do:

  • call collect to initialize the attributes.
  • call propagate to propagate the variance constraints.
  • call print_stats to print the results.

Introduced properties

private var _contravar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: _contravar_classes

The set of generic types found in a contravariant (and invariant) position
private var _contravar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: _contravar_pt

The set of formal parameters found in a contravariant (and invariant) position
private var _covar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: _covar_classes

The set of generic types found in a covariant (and invariant) position
private var _covar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: _covar_pt

The set of formal parameters found in a covariant (and invariant) position
private var _cpt_class: Counter[String]

nitc :: DetectVarianceConstraints :: _cpt_class

Classes by number of formal parameters
private var _exclude_private: Bool

nitc :: DetectVarianceConstraints :: _exclude_private

Does the collect exclude private properties?
private var _pts: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: _pts

The set of all collected formal parameters
fun collect(mainmodule: MModule)

nitc :: DetectVarianceConstraints :: collect

Collect all types used in covariant and contravariant positions.
fun contravar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: contravar_classes

The set of generic types found in a contravariant (and invariant) position
protected fun contravar_classes=(contravar_classes: HashSet[MClassType])

nitc :: DetectVarianceConstraints :: contravar_classes=

The set of generic types found in a contravariant (and invariant) position
fun contravar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: contravar_pt

The set of formal parameters found in a contravariant (and invariant) position
protected fun contravar_pt=(contravar_pt: HashSet[MParameterType])

nitc :: DetectVarianceConstraints :: contravar_pt=

The set of formal parameters found in a contravariant (and invariant) position
fun covar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: covar_classes

The set of generic types found in a covariant (and invariant) position
protected fun covar_classes=(covar_classes: HashSet[MClassType])

nitc :: DetectVarianceConstraints :: covar_classes=

The set of generic types found in a covariant (and invariant) position
fun covar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: covar_pt

The set of formal parameters found in a covariant (and invariant) position
protected fun covar_pt=(covar_pt: HashSet[MParameterType])

nitc :: DetectVarianceConstraints :: covar_pt=

The set of formal parameters found in a covariant (and invariant) position
fun cpt_class: Counter[String]

nitc :: DetectVarianceConstraints :: cpt_class

Classes by number of formal parameters
protected fun cpt_class=(cpt_class: Counter[String])

nitc :: DetectVarianceConstraints :: cpt_class=

Classes by number of formal parameters
fun exclude_private: Bool

nitc :: DetectVarianceConstraints :: exclude_private

Does the collect exclude private properties?
protected fun exclude_private=(exclude_private: Bool)

nitc :: DetectVarianceConstraints :: exclude_private=

Does the collect exclude private properties?
fun print_stats

nitc :: DetectVarianceConstraints :: print_stats

Print the final stats on the screen
fun propagate

nitc :: DetectVarianceConstraints :: propagate

Propagate the variance constraints on covar_classes, covar_pt, contravar_classes and contravar_pt
fun pts: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: pts

The set of all collected formal parameters
protected fun pts=(pts: HashSet[MParameterType])

nitc :: DetectVarianceConstraints :: pts=

The set of all collected formal parameters

Redefined properties

redef type SELF: DetectVarianceConstraints

nitc $ DetectVarianceConstraints :: SELF

Type of this instance, automatically specialized in every class

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
private var _contravar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: _contravar_classes

The set of generic types found in a contravariant (and invariant) position
private var _contravar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: _contravar_pt

The set of formal parameters found in a contravariant (and invariant) position
private var _covar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: _covar_classes

The set of generic types found in a covariant (and invariant) position
private var _covar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: _covar_pt

The set of formal parameters found in a covariant (and invariant) position
private var _cpt_class: Counter[String]

nitc :: DetectVarianceConstraints :: _cpt_class

Classes by number of formal parameters
private var _exclude_private: Bool

nitc :: DetectVarianceConstraints :: _exclude_private

Does the collect exclude private properties?
private var _pts: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: _pts

The set of all collected formal parameters
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun collect(mainmodule: MModule)

nitc :: DetectVarianceConstraints :: collect

Collect all types used in covariant and contravariant positions.
fun contravar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: contravar_classes

The set of generic types found in a contravariant (and invariant) position
protected fun contravar_classes=(contravar_classes: HashSet[MClassType])

nitc :: DetectVarianceConstraints :: contravar_classes=

The set of generic types found in a contravariant (and invariant) position
fun contravar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: contravar_pt

The set of formal parameters found in a contravariant (and invariant) position
protected fun contravar_pt=(contravar_pt: HashSet[MParameterType])

nitc :: DetectVarianceConstraints :: contravar_pt=

The set of formal parameters found in a contravariant (and invariant) position
fun covar_classes: HashSet[MClassType]

nitc :: DetectVarianceConstraints :: covar_classes

The set of generic types found in a covariant (and invariant) position
protected fun covar_classes=(covar_classes: HashSet[MClassType])

nitc :: DetectVarianceConstraints :: covar_classes=

The set of generic types found in a covariant (and invariant) position
fun covar_pt: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: covar_pt

The set of formal parameters found in a covariant (and invariant) position
protected fun covar_pt=(covar_pt: HashSet[MParameterType])

nitc :: DetectVarianceConstraints :: covar_pt=

The set of formal parameters found in a covariant (and invariant) position
fun cpt_class: Counter[String]

nitc :: DetectVarianceConstraints :: cpt_class

Classes by number of formal parameters
protected fun cpt_class=(cpt_class: Counter[String])

nitc :: DetectVarianceConstraints :: cpt_class=

Classes by number of formal parameters
fun exclude_private: Bool

nitc :: DetectVarianceConstraints :: exclude_private

Does the collect exclude private properties?
protected fun exclude_private=(exclude_private: Bool)

nitc :: DetectVarianceConstraints :: exclude_private=

Does the collect exclude private properties?
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
private intern fun native_class_name: CString

core :: Object :: native_class_name

The class name of the object in CString format.
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun print_stats

nitc :: DetectVarianceConstraints :: print_stats

Print the final stats on the screen
fun propagate

nitc :: DetectVarianceConstraints :: propagate

Propagate the variance constraints on covar_classes, covar_pt, contravar_classes and contravar_pt
fun pts: HashSet[MParameterType]

nitc :: DetectVarianceConstraints :: pts

The set of all collected formal parameters
protected fun pts=(pts: HashSet[MParameterType])

nitc :: DetectVarianceConstraints :: pts=

The set of all collected formal parameters
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_s: String

core :: Object :: to_s

User readable representation of self.
package_diagram nitc::DetectVarianceConstraints DetectVarianceConstraints core::Object Object nitc::DetectVarianceConstraints->core::Object

Parents

interface Object

core :: Object

The root of the class hierarchy.

Class definitions

nitc $ DetectVarianceConstraints
# A specific analysis that detects the variance constraints of formal parameters.
#
# The client has 3 steps to do:
#
#  * call `collect` to initialize the attributes.
#  * call `propagate` to propagate the variance constraints.
#  * call `print_stats` to print the results.
class DetectVarianceConstraints
	# Collect all types used in covariant and contravariant positions.
	#
	# The collect visits all classes and properties of `mainmodule` and its imported modules.
	#
	# After the visit, the attributes of `self` are filled.
	fun collect(mainmodule: MModule)
	do
		for m in mainmodule.in_importation.greaters do
			for cd in m.mclassdefs do
				if cd.is_intro then
					pts.add_all(cd.mclass.mparameters)
					var a = cd.mclass.arity
					if a == 0 then
						cpt_class.inc("non generic")
					else if a == 1 then
						cpt_class.inc("with 1 formal type parameter")
					else
						cpt_class.inc("with {a} formal type parameters")
					end
				end
				for t in cd.supertypes do
					# Supertype (covariant)
					if t.need_anchor then covar_classes.add(t)
				end
				for pd in cd.mpropdefs do
					if exclude_private and pd.mproperty.visibility <= private_visibility then continue
					if pd isa MMethodDef then
						# Parameters (contravariant)
						for p in pd.msignature.mparameters do
							var t = p.mtype.undecorate
							if not t.need_anchor then
								# OK
							else if t isa MParameterType then
								contravar_pt.add(t)
							else if t isa MVirtualType then
								# TODO?
							else if t isa MClassType then
								contravar_classes.add(t)
							else
								abort
							end
						end
						# Return (covariant)
						var t = pd.msignature.return_mtype
						if t != null and t.need_anchor then
							t = t.undecorate
							if t isa MParameterType then
								covar_pt.add(t)
							else if t isa MVirtualType then
								# TODO?
							else if t isa MClassType then
								covar_classes.add(t)
							else
								abort
							end
						end
					else if pd isa MAttributeDef then
						# Attribute (invariant)
						var t = pd.static_mtype
						if t != null and t.need_anchor then
							t = t.undecorate
							if t isa MParameterType then
								covar_pt.add t
								contravar_pt.add t
							else if t isa MVirtualType then
								# TODO?
							else if t isa MClassType then
								covar_classes.add(t)
								contravar_classes.add(t)
							else
								abort
							end
						end
					else if pd isa MVirtualTypeDef then
						# Virtual type bound (covariant)
						var t = pd.bound
						if t != null and t.need_anchor then
							t = t.undecorate
							if t isa MParameterType then
								covar_pt.add t
							else if t isa MVirtualType then
								# TODO?
							else if t isa MClassType then
								covar_classes.add(t)
							else
								abort
							end
						end
					end
				end
			end
		end
	end

	# The set of all collected formal parameters
	var pts = new HashSet[MParameterType]

	# The set of generic types found in a covariant (and invariant) position
	var covar_classes = new HashSet[MClassType]

	# The set of formal parameters found in a covariant (and invariant) position
	var covar_pt = new HashSet[MParameterType]

	# The set of generic types found in a contravariant (and invariant) position
	var contravar_classes = new HashSet[MClassType]

	# The set of formal parameters found in a contravariant (and invariant) position
	var contravar_pt = new HashSet[MParameterType]

	# Classes by number of formal parameters
	var cpt_class = new Counter[String]

	# Does the collect exclude private properties?
	# Default is `false`
	var exclude_private = false

	# Propagate the variance constraints on `covar_classes`, `covar_pt`, `contravar_classes` and `contravar_pt`
	#
	# The algorithm uses a fixed-point approach on the covariance/contravariance rules.
	fun propagate
	do
		# Classes to add to the `covar_classes` set at the end of an iteration
		var new_covar = new Array[MClassType]
		# Classes to add to the `contravar_classes` set at the end of an iteration
		var new_contravar = new Array[MClassType]
		# Does a modification occurred, so that another iteration is needed?
		var dirty = true
		# Total number of iterations
		var cpt = 0

		while dirty do
			cpt += 1
			dirty = false
			new_covar.clear
			new_contravar.clear

			# Process the generic types in a covariant position
			for c in covar_classes do for i in [0..c.mclass.arity[ do
				# The type used in the argument
				var ta = c.arguments[i].undecorate
				# The associated formal parameter
				var tp = c.mclass.mparameters[i]

				if not ta.need_anchor then
					# Nothing to do
				else if ta isa MParameterType then
					# COVAR * COVAR = COVAR
					if covar_pt.has(tp) and not covar_pt.has(ta) then
						covar_pt.add(ta)
						dirty = true
					end
					# COVAR * CONTRAVAR = CONTRAVAR
					if contravar_pt.has(tp) and not contravar_pt.has(ta) then
						contravar_pt.add(ta)
						dirty = true
					end
				else if ta isa MVirtualType then
					# TODO?
				else if ta isa MClassType then
					# COVAR * COVAR = COVAR
					if covar_pt.has(tp) and not covar_classes.has(ta) then
						new_covar.add ta
						dirty = true
					end
					# COVAR * CONTRAVAR = CONTRAVAR
					if contravar_pt.has(tp) and not contravar_classes.has(ta) then
						new_contravar.add ta
						dirty = true
					end
				end
			end

			# Process the generic types in a contravariant position
			for c in contravar_classes do for i in [0..c.mclass.arity[ do
				# The type used in the argument
				var ta = c.arguments[i].undecorate
				# The associated formal parameter
				var tp = c.mclass.mparameters[i]

				if not ta.need_anchor then
					# Nothing to do
				else if ta isa MParameterType then
					# CONTRAVAR * CONTRAVAR = COVAR
					if contravar_pt.has(tp) and not covar_pt.has(ta) then
						covar_pt.add(ta)
						dirty = true
					end
					# CONTRAVAR * COVAR = CONTRAVAR
					if covar_pt.has(tp) and not contravar_pt.has(ta) then
						contravar_pt.add(ta)
						dirty = true
					end
				else if ta isa MVirtualType then
					# TODO?
				else if ta isa MClassType then
					# CONTRAVAR * CONTRAVAR = COVAR
					if contravar_pt.has(tp) and not covar_classes.has(ta) then
						new_covar.add ta
						dirty = true
					end
					# CONTRAVAR * COVAR = CONTRAVAR
					if covar_pt.has(tp) and not contravar_classes.has(ta) then
						new_contravar.add ta
						dirty = true
					end
				end
			end

			covar_classes.add_all(new_covar)
			contravar_classes.add_all(new_contravar)
		end
	end

	# Print the final stats on the screen
	fun print_stats
	do
		var nb_cov = 0
		var nb_con = 0
		var nb_inv = 0
		var nb_biv = 0

		for pt in pts do
			if covar_pt.has(pt) then
				if contravar_pt.has(pt) then
					nb_inv += 1
				else
					nb_cov += 1
					#print "covar: {pt.full_name}"
				end
			else
				if contravar_pt.has(pt) then
					nb_con += 1
					#print "contravar: {pt.full_name}"
				else
					nb_biv += 1
					#print "bivar: {pt.full_name}"
				end
			end
		end

		print "  covariants: {nb_cov} ({div(nb_cov*100, pts.length)}%)"
		print "  contravariants: {nb_con} ({div(nb_con*100, pts.length)}%)"
		print "  bivariants: {nb_biv} ({div(nb_biv*100, pts.length)}%)"
		print "  invariants: {nb_inv} ({div(nb_inv*100, pts.length)}%)"
		print "  total: {pts.length}"
	end
end
src/metrics/detect_variance_constraints.nit:79,1--333,3