src/model: remove extra } from `MGenericType::to_s`
[nit.git] / src / model / model.nit
index fa9c4d4..b483543 100644 (file)
@@ -74,11 +74,7 @@ redef class Model
        # Visibility or modules are not considered
        fun get_mclasses_by_name(name: String): nullable Array[MClass]
        do
-               if mclasses_by_name.has_key(name) then
-                       return mclasses_by_name[name]
-               else
-                       return null
-               end
+               return mclasses_by_name.get_or_null(name)
        end
 
        # Collections of properties grouped by their short name
@@ -92,11 +88,7 @@ redef class Model
        # Visibility or modules are not considered
        fun get_mproperties_by_name(name: String): nullable Array[MProperty]
        do
-               if not mproperties_by_name.has_key(name) then
-                       return null
-               else
-                       return mproperties_by_name[name]
-               end
+               return mproperties_by_name.get_or_null(name)
        end
 
        # The only null type
@@ -251,9 +243,13 @@ redef class MModule
        do
                var cla = self.model.get_mclasses_by_name(name)
                if cla == null then
-                       if name == "Bool" then
+                       if name == "Bool" and self.model.get_mclasses_by_name("Object") != null then
+                               # Bool is injected because it is needed by engine to code the result
+                               # of the implicit casts.
                                var c = new MClass(self, name, null, enum_kind, public_visibility)
                                var cladef = new MClassDef(self, c.mclass_type, new Location(null, 0,0,0,0))
+                               cladef.set_supertypes([object_type])
+                               cladef.add_in_hierarchy
                                return c
                        end
                        print("Fatal Error: no primitive class {name}")
@@ -263,7 +259,7 @@ redef class MModule
                        var msg = "Fatal Error: more than one primitive class {name}:"
                        for c in cla do msg += " {c.full_name}"
                        print msg
-                       exit(1)
+                       #exit(1)
                end
                return cla.first
        end
@@ -354,10 +350,15 @@ class MClass
        redef var name: String
 
        # The canonical name of the class
+       #
+       # It is the name of the class prefixed by the full_name of the `intro_mmodule`
        # Example: `"owner::module::MyClass"`
-       fun full_name: String
-       do
-               return "{self.intro_mmodule.full_name}::{name}"
+       redef var full_name is lazy do
+               return "{self.intro_mmodule.namespace_for(visibility)}::{name}"
+       end
+
+       redef var c_name is lazy do
+               return "{intro_mmodule.c_namespace_for(visibility)}__{name.to_cmangle}"
        end
 
        # The number of generic formal parameters
@@ -368,6 +369,7 @@ class MClass
        # is empty if the class is not generic
        var mparameters = new Array[MParameterType]
 
+       # Initialize `mparameters` from their names.
        protected fun setup_parameter_names(parameter_names: nullable Array[String]) is
                autoinit
        do
@@ -524,6 +526,41 @@ class MClassDef
        # Actually the name of the `mclass`
        redef fun name do return mclass.name
 
+       # The module and class name separated by a '#'.
+       #
+       # The short-name of the class is used for introduction.
+       # Example: "my_module#MyClass"
+       #
+       # The full-name of the class is used for refinement.
+       # Example: "my_module#intro_module::MyClass"
+       redef var full_name is lazy do
+               if is_intro then
+                       # public gives 'p#A'
+                       # private gives 'p::m#A'
+                       return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}"
+               else if mclass.intro_mmodule.mproject != mmodule.mproject then
+                       # public gives 'q::n#p::A'
+                       # private gives 'q::n#p::m::A'
+                       return "{mmodule.full_name}#{mclass.full_name}"
+               else if mclass.visibility > private_visibility then
+                       # public gives 'p::n#A'
+                       return "{mmodule.full_name}#{mclass.name}"
+               else
+                       # private gives 'p::n#::m::A' (redundant p is omitted)
+                       return "{mmodule.full_name}#::{mclass.intro_mmodule.name}::{mclass.name}"
+               end
+       end
+
+       redef var c_name is lazy do
+               if is_intro then
+                       return "{mmodule.c_namespace_for(mclass.visibility)}___{mclass.c_name}"
+               else if mclass.intro_mmodule.mproject == mmodule.mproject and mclass.visibility > private_visibility then
+                       return "{mmodule.c_name}___{mclass.name.to_cmangle}"
+               else
+                       return "{mmodule.c_name}___{mclass.c_name}"
+               end
+       end
+
        redef fun model do return mmodule.model
 
        # All declared super-types
@@ -628,24 +665,18 @@ abstract class MType
        do
                var sub = self
                if sub == sup then return true
+
+               #print "1.is {sub} a {sup}? ===="
+
                if anchor == null then
                        assert not sub.need_anchor
                        assert not sup.need_anchor
                else
+                       # First, resolve the formal types to the simplest equivalent forms in the receiver
                        assert sub.can_resolve_for(anchor, null, mmodule)
+                       sub = sub.lookup_fixed(mmodule, anchor)
                        assert sup.can_resolve_for(anchor, null, mmodule)
-               end
-
-               # First, resolve the formal types to a common version in the receiver
-               # The trick here is that fixed formal type will be associated to the bound
-               # And unfixed formal types will be associated to a canonical formal type.
-               if sub isa MParameterType or sub isa MVirtualType then
-                       assert anchor != null
-                       sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, false)
-               end
-               if sup isa MParameterType or sup isa MVirtualType then
-                       assert anchor != null
-                       sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mmodule, false)
+                       sup = sup.lookup_fixed(mmodule, anchor)
                end
 
                # Does `sup` accept null or not?
@@ -669,15 +700,17 @@ abstract class MType
                end
                # Now the case of direct null and nullable is over.
 
-               # A unfixed formal type can only accept itself
-               if sup isa MParameterType or sup isa MVirtualType then
-                       return sub == sup
-               end
-
                # If `sub` is a formal type, then it is accepted if its bound is accepted
-               if sub isa MParameterType or sub isa MVirtualType then
+               while sub isa MParameterType or sub isa MVirtualType do
+                       #print "3.is {sub} a {sup}?"
+
+                       # A unfixed formal type can only accept itself
+                       if sub == sup then return true
+
                        assert anchor != null
-                       sub = sub.anchor_to(mmodule, anchor)
+                       sub = sub.lookup_bound(mmodule, anchor)
+
+                       #print "3.is {sub} a {sup}?"
 
                        # Manage the second layer of null/nullable
                        if sub isa MNullableType then
@@ -687,9 +720,15 @@ abstract class MType
                                return sup_accept_null
                        end
                end
+               #print "4.is {sub} a {sup}? <- no more resolution"
 
                assert sub isa MClassType # It is the only remaining type
 
+               # A unfixed formal type can only accept itself
+               if sup isa MParameterType or sup isa MVirtualType then
+                       return false
+               end
+
                if sup isa MNullType then
                        # `sup` accepts only null
                        return false
@@ -878,6 +917,28 @@ abstract class MType
        # ENSURE: `not self.need_anchor implies result == self`
        fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MType is abstract
 
+       # Resolve formal type to its verbatim bound.
+       # If the type is not formal, just return self
+       #
+       # The result is returned exactly as declared in the "type" property (verbatim).
+       # So it could be another formal type.
+       #
+       # In case of conflict, the method aborts.
+       fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType do return self
+
+       # Resolve the formal type to its simplest equivalent form.
+       #
+       # Formal types are either free or fixed.
+       # When it is fixed, it means that it is equivalent with a simpler type.
+       # When a formal type is free, it means that it is only equivalent with itself.
+       # This method return the most simple equivalent type of `self`.
+       #
+       # This method is mainly used for subtype test in order to sanely compare fixed.
+       #
+       # By default, return self.
+       # See the redefinitions for specific behavior in each kind of type.
+       fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType do return self
+
        # Can the type be resolved?
        #
        # In order to resolve open types, the formal types must make sence.
@@ -1007,6 +1068,10 @@ class MClassType
 
        redef fun to_s do return mclass.to_s
 
+       redef fun full_name do return mclass.full_name
+
+       redef fun c_name do return mclass.c_name
+
        redef fun need_anchor do return false
 
        redef fun anchor_to(mmodule: MModule, anchor: MClassType): MClassType
@@ -1030,14 +1095,21 @@ class MClassType
 
        redef fun collect_mclasses(mmodule)
        do
+               if collect_mclasses_last_module == mmodule then return collect_mclasses_last_module_cache
                assert not self.need_anchor
                var cache = self.collect_mclasses_cache
                if not cache.has_key(mmodule) then
                        self.collect_things(mmodule)
                end
-               return cache[mmodule]
+               var res = cache[mmodule]
+               collect_mclasses_last_module = mmodule
+               collect_mclasses_last_module_cache = res
+               return res
        end
 
+       private var collect_mclasses_last_module: nullable MModule = null
+       private var collect_mclasses_last_module_cache: Set[MClass] is noinit
+
        redef fun collect_mtypes(mmodule)
        do
                assert not self.need_anchor
@@ -1108,10 +1180,30 @@ class MGenericType
                self.to_s = "{mclass}[{arguments.join(", ")}]"
        end
 
-       # Recursively print the type of the arguments within brackets.
+       # The short-name of the class, then the full-name of each type arguments within brackets.
        # Example: `"Map[String, List[Int]]"`
        redef var to_s: String is noinit
 
+       # The full-name of the class, then the full-name of each type arguments within brackets.
+       # Example: `"standard::Map[standard::String, standard::List[standard::Int]]"`
+       redef var full_name is lazy do
+               var args = new Array[String]
+               for t in arguments do
+                       args.add t.full_name
+               end
+               return "{mclass.full_name}[{args.join(", ")}]"
+       end
+
+       redef var c_name is lazy do
+               var res = mclass.c_name
+               # Note: because the arity is known, a prefix notation is enough
+               for t in arguments do
+                       res += "__"
+                       res += t.c_name
+               end
+               return res.to_s
+       end
+
        redef var need_anchor: Bool is noinit
 
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
@@ -1161,86 +1253,85 @@ class MVirtualType
 
        # The property associated with the type.
        # Its the definitions of this property that determine the bound or the virtual type.
-       var mproperty: MProperty
+       var mproperty: MVirtualTypeProp
 
        redef fun model do return self.mproperty.intro_mclassdef.mmodule.model
 
-       # Lookup the bound for a given resolved_receiver
-       # The result may be a other virtual type (or a parameter type)
-       #
-       # The result is returned exactly as declared in the "type" property (verbatim).
-       #
-       # In case of conflict, the method aborts.
-       fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+       redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+       do
+               return lookup_single_definition(mmodule, resolved_receiver).bound.as(not null)
+       end
+
+       private fun lookup_single_definition(mmodule: MModule, resolved_receiver: MType): MVirtualTypeDef
        do
                assert not resolved_receiver.need_anchor
                var props = self.mproperty.lookup_definitions(mmodule, resolved_receiver)
                if props.is_empty then
                        abort
                else if props.length == 1 then
-                       return props.first.as(MVirtualTypeDef).bound.as(not null)
+                       return props.first
                end
                var types = new ArraySet[MType]
+               var res  = props.first
                for p in props do
-                       types.add(p.as(MVirtualTypeDef).bound.as(not null))
+                       types.add(p.bound.as(not null))
+                       if not res.is_fixed then res = p
                end
                if types.length == 1 then
-                       return types.first
+                       return res
                end
                abort
        end
 
-       # Is the virtual type fixed for a given resolved_receiver?
-       fun is_fixed(mmodule: MModule, resolved_receiver: MType): Bool
+       # A VT is fixed when:
+       # * the VT is (re-)defined with the annotation `is fixed`
+       # * the VT is (indirectly) bound to an enum class (see `enum_kind`) since there is no subtype possible
+       # * the receiver is an enum class since there is no subtype possible
+       redef fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType
        do
                assert not resolved_receiver.need_anchor
-               var props = self.mproperty.lookup_definitions(mmodule, resolved_receiver)
-               if props.is_empty then
-                       abort
-               end
-               for p in props do
-                       if p.as(MVirtualTypeDef).is_fixed then return true
-               end
-               return false
+               resolved_receiver = resolved_receiver.as_notnullable
+               assert resolved_receiver isa MClassType # It is the only remaining type
+
+               var prop = lookup_single_definition(mmodule, resolved_receiver)
+               var res = prop.bound.as(not null)
+
+               # Recursively lookup the fixed result
+               res = res.lookup_fixed(mmodule, resolved_receiver)
+
+               # 1. For a fixed VT, return the resolved bound
+               if prop.is_fixed then return res
+
+               # 2. For a enum boud, return the bound
+               if res isa MClassType and res.mclass.kind == enum_kind then return res
+
+               # 3. for a enum receiver return the bound
+               if resolved_receiver.mclass.kind == enum_kind then return res
+
+               return self
        end
 
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
        do
+               if not cleanup_virtual then return self
                assert can_resolve_for(mtype, anchor, mmodule)
                # self is a virtual type declared (or inherited) in mtype
                # The point of the function it to get the bound of the virtual type that make sense for mtype
                # But because mtype is maybe a virtual/formal type, we need to get a real receiver first
                #print "{class_name}: {self}/{mtype}/{anchor}?"
-               var resolved_reciever
+               var resolved_receiver
                if mtype.need_anchor then
                        assert anchor != null
-                       resolved_reciever = mtype.resolve_for(anchor, null, mmodule, true)
+                       resolved_receiver = mtype.resolve_for(anchor, null, mmodule, true)
                else
-                       resolved_reciever = mtype
+                       resolved_receiver = mtype
                end
                # Now, we can get the bound
-               var verbatim_bound = lookup_bound(mmodule, resolved_reciever)
+               var verbatim_bound = lookup_bound(mmodule, resolved_receiver)
                # The bound is exactly as declared in the "type" property, so we must resolve it again
                var res = verbatim_bound.resolve_for(mtype, anchor, mmodule, cleanup_virtual)
-               #print "{class_name}: {self}/{mtype}/{anchor} -> {self}/{resolved_receiver}/{anchor} -> {verbatim_bound}/{mtype}/{anchor} -> {res}"
-
-               # What to return here? There is a bunch a special cases:
-               # If 'cleanup_virtual' we must return the resolved type, since we cannot return self
-               if cleanup_virtual then return res
-               # If the receiver is a intern class, then the virtual type cannot be redefined since there is no possible subclass. self is just fixed. so simply return the resolution
-               if resolved_reciever isa MNullableType then resolved_reciever = resolved_reciever.mtype
-               if resolved_reciever.as(MClassType).mclass.kind == enum_kind then return res
-               # If the resolved type isa MVirtualType, it means that self was bound to it, and cannot be unbound. self is just fixed. so return the resolution.
-               if res isa MVirtualType then return res
-               # If we are final, just return the resolution
-               if is_fixed(mmodule, resolved_reciever) then return res
-               # If the resolved type isa intern class, then there is no possible valid redefinition in any potential subclass. self is just fixed. so simply return the resolution
-               if res isa MClassType and res.mclass.kind == enum_kind then return res
-               # TODO: What if bound to a MParameterType?
-               # Note that Nullable types can always be redefined by the non nullable version, so there is no specific case on it.
 
-               # If anything apply, then `self' cannot be resolved, so return self
-               return self
+               return res
        end
 
        redef fun can_resolve_for(mtype, anchor, mmodule)
@@ -1253,6 +1344,10 @@ class MVirtualType
        end
 
        redef fun to_s do return self.mproperty.to_s
+
+       redef fun full_name do return self.mproperty.full_name
+
+       redef fun c_name do return self.mproperty.c_name
 end
 
 # The type associated to a formal parameter generic type of a class
@@ -1297,12 +1392,19 @@ class MParameterType
 
        redef fun to_s do return name
 
-       # Resolve the bound for a given resolved_receiver
-       # The result may be a other virtual type (or a parameter type)
-       fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
+       redef var full_name is lazy do return "{mclass.full_name}::{name}"
+
+       redef var c_name is lazy do return mclass.c_name + "__" + "#{name}".to_cmangle
+
+       redef fun lookup_bound(mmodule: MModule, resolved_receiver: MType): MType
        do
                assert not resolved_receiver.need_anchor
+               resolved_receiver = resolved_receiver.as_notnullable
+               assert resolved_receiver isa MClassType # It is the only remaining type
                var goalclass = self.mclass
+               if resolved_receiver.mclass == goalclass then
+                       return resolved_receiver.arguments[self.rank]
+               end
                var supertypes = resolved_receiver.collect_mtypes(mmodule)
                for t in supertypes do
                        if t.mclass == goalclass then
@@ -1315,6 +1417,22 @@ class MParameterType
                abort
        end
 
+       # A PT is fixed when:
+       # * Its bound is a enum class (see `enum_kind`).
+       #   The PT is just useless, but it is still a case.
+       # * More usually, the `resolved_receiver` is a subclass of `self.mclass`,
+       #   so it is necessarily fixed in a `super` clause, either with a normal type
+       #   or with another PT.
+       #   See `resolve_for` for examples about related issues.
+       redef fun lookup_fixed(mmodule: MModule, resolved_receiver: MType): MType
+       do
+               assert not resolved_receiver.need_anchor
+               resolved_receiver = resolved_receiver.as_notnullable
+               assert resolved_receiver isa MClassType # It is the only remaining type
+               var res = self.resolve_for(resolved_receiver.mclass.mclass_type, resolved_receiver, mmodule, false)
+               return res
+       end
+
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
        do
                assert can_resolve_for(mtype, anchor, mmodule)
@@ -1347,7 +1465,7 @@ class MParameterType
                        resolved_receiver = anchor.arguments[resolved_receiver.rank]
                        if resolved_receiver isa MNullableType then resolved_receiver = resolved_receiver.mtype
                end
-               assert resolved_receiver isa MClassType
+               assert resolved_receiver isa MClassType # It is the only remaining type
 
                # Eh! The parameter is in the current class.
                # So we return the corresponding argument, no mater what!
@@ -1397,6 +1515,10 @@ class MNullableType
 
        redef var to_s: String is noinit
 
+       redef var full_name is lazy do return "nullable {mtype.full_name}"
+
+       redef var c_name is lazy do return "nullable__{mtype.c_name}"
+
        redef fun need_anchor do return mtype.need_anchor
        redef fun as_nullable do return self
        redef fun as_notnullable do return mtype
@@ -1411,6 +1533,14 @@ class MNullableType
                return self.mtype.can_resolve_for(mtype, anchor, mmodule)
        end
 
+       # Efficiently returns `mtype.lookup_fixed(mmodule, resolved_receiver).as_nullable`
+       redef fun lookup_fixed(mmodule, resolved_receiver)
+       do
+               var t = mtype.lookup_fixed(mmodule, resolved_receiver)
+               if t == mtype then return self
+               return t.as_nullable
+       end
+
        redef fun depth do return self.mtype.depth
 
        redef fun length do return self.mtype.length
@@ -1441,6 +1571,8 @@ class MNullType
        super MType
        redef var model: Model
        redef fun to_s do return "null"
+       redef fun full_name do return "null"
+       redef fun c_name do return "null"
        redef fun as_nullable do return self
        redef fun need_anchor do return false
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
@@ -1570,6 +1702,8 @@ class MParameter
                end
        end
 
+       # Returns a new parameter with the `mtype` resolved.
+       # See `MType::resolve_for` for details.
        fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MParameter
        do
                if not self.mtype.need_anchor then return self
@@ -1607,16 +1741,25 @@ abstract class MProperty
        # The (short) name of the property
        redef var name: String
 
-       # The canonical name of the property
-       # Example: "owner::my_module::MyClass::my_method"
-       fun full_name: String
-       do
-               return "{self.intro_mclassdef.mmodule.full_name}::{self.intro_mclassdef.mclass.name}::{name}"
+       # The canonical name of the property.
+       #
+       # It is the short-`name` prefixed by the short-name of the class and the full-name of the module.
+       # Example: "my_project::my_module::MyClass::my_method"
+       redef var full_name is lazy do
+               return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
+       end
+
+       redef var c_name is lazy do
+               # FIXME use `namespace_for`
+               return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.name.to_cmangle}__{name.to_cmangle}"
        end
 
        # The visibility of the property
        var visibility: MVisibility
 
+       # Is the property usable as an initializer?
+       var is_autoinit = false is writable
+
        init
        do
                intro_mclassdef.intro_mproperties.add(self)
@@ -1647,6 +1790,9 @@ abstract class MProperty
        # If mtype does not know mproperty then an empty array is returned.
        #
        # If you want the really most specific property, then look at `lookup_first_definition`
+       #
+       # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
+       # ENSURE: `not mtype.has_mproperty(mmodule, self) == result.is_empty`
        fun lookup_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
        do
                assert not mtype.need_anchor
@@ -1685,7 +1831,8 @@ abstract class MProperty
        #
        # If you want the really most specific property, then look at `lookup_next_definition`
        #
-       # FIXME: Move to `MPropDef`?
+       # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
+       # ENSURE: `not mtype.has_mproperty(mmodule, self) implies result.is_empty`
        fun lookup_super_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
        do
                assert not mtype.need_anchor
@@ -1753,24 +1900,28 @@ abstract class MProperty
        #
        # FIXME: the linearization is still unspecified
        #
-       # REQUIRE: `not mtype.need_anchor`
+       # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
        # REQUIRE: `mtype.has_mproperty(mmodule, self)`
        fun lookup_first_definition(mmodule: MModule, mtype: MType): MPROPDEF
        do
-               assert mtype.has_mproperty(mmodule, self)
                return lookup_all_definitions(mmodule, mtype).first
        end
 
        # Return all definitions in a linearization order
        # Most specific first, most general last
+       #
+       # REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
+       # REQUIRE: `mtype.has_mproperty(mmodule, self)`
        fun lookup_all_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
        do
-               assert not mtype.need_anchor
                mtype = mtype.as_notnullable
 
                var cache = self.lookup_all_definitions_cache[mmodule, mtype]
                if cache != null then return cache
 
+               assert not mtype.need_anchor
+               assert mtype.has_mproperty(mmodule, self)
+
                #print "select prop {mproperty} for {mtype} in {self}"
                # First, select all candidates
                var candidates = new Array[MPROPDEF]
@@ -1882,6 +2033,75 @@ abstract class MPropDef
        # Actually the name of the `mproperty`
        redef fun name do return mproperty.name
 
+       # The full-name of mpropdefs combine the information about the `classdef` and the `mproperty`.
+       #
+       # Therefore the combination of identifiers is awful,
+       # the worst case being
+       #
+       #  * a property "p::m::A::x"
+       #  * redefined in a refinement of a class "q::n::B"
+       #  * in a module "r::o"
+       #  * so "r::o#q::n::B#p::m::A::x"
+       #
+       # Fortunately, the full-name is simplified when entities are repeated.
+       # For the previous case, the simplest form is "p#A#x".
+       redef var full_name is lazy do
+               var res = new FlatBuffer
+
+               # The first part is the mclassdef. Worst case is "r::o#q::n::B"
+               res.append mclassdef.full_name
+
+               res.append "#"
+
+               if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
+                       # intro are unambiguous in a class
+                       res.append name
+               else
+                       # Just try to simplify each part
+                       if mclassdef.mmodule.mproject != mproperty.intro_mclassdef.mmodule.mproject then
+                               # precise "p::m" only if "p" != "r"
+                               res.append mproperty.intro_mclassdef.mmodule.full_name
+                               res.append "::"
+                       else if mproperty.visibility <= private_visibility then
+                               # Same project ("p"=="q"), but private visibility,
+                               # does the module part ("::m") need to be displayed
+                               if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mproject then
+                                       res.append "::"
+                                       res.append mproperty.intro_mclassdef.mmodule.name
+                                       res.append "::"
+                               end
+                       end
+                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
+                               # precise "B" only if not the same class than "A"
+                               res.append mproperty.intro_mclassdef.name
+                               res.append "::"
+                       end
+                       # Always use the property name "x"
+                       res.append mproperty.name
+               end
+               return res.to_s
+       end
+
+       redef var c_name is lazy do
+               var res = new FlatBuffer
+               res.append mclassdef.c_name
+               res.append "___"
+               if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
+                       res.append name.to_cmangle
+               else
+                       if mclassdef.mmodule != mproperty.intro_mclassdef.mmodule then
+                               res.append mproperty.intro_mclassdef.mmodule.c_name
+                               res.append "__"
+                       end
+                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
+                               res.append mproperty.intro_mclassdef.name.to_cmangle
+                               res.append "__"
+                       end
+                       res.append mproperty.name.to_cmangle
+               end
+               return res.to_s
+       end
+
        redef fun model do return mclassdef.model
 
        # Internal name combining the module, the class and the property