MODULE************************************************************************* Copyright (C) Olivetti 1989 All Rights reserved Use and copy of this software and preparation of derivative works based upon this software are permitted to any person, provided this same copyright notice and the following Olivetti warranty disclaimer are included in any copy of the software or any modification thereof or derivative work therefrom made by any person. This software is made available AS IS and Olivetti disclaims all warranties with respect to this software, whether expressed or implied under any law, including all implied warranties of merchantibility and fitness for any purpose. In no event shall Olivetti be liable for any damages whatsoever resulting from loss of use, data or profits or otherwise arising out of or in connection with the use or performance of this software. *************************************************************************; M3CTypeRelation
IMPORT M3AST_AS, M3AST_SM, M3ASTNext; IMPORT M3AST_AS_F, M3AST_SM_F; IMPORT M3CTypesMisc, M3CConcTypeSpec, M3CRaisesSet, M3CTypeCompare, M3COrdinal; IMPORT M3CExpValue, M3CBackEnd; <*INLINE*> PROCEDUREProcedures for checking that one type is a subtype of another. Here are the subtyping rules, extracted from the M3 report:Identical ( type1, type2: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= BEGIN RETURN M3CTypeCompare.Identical(type1, type2); END Identical; PROCEDUREIdenticalM3TYPEs (m1, m2: M3AST_AS.M3TYPE): BOOLEAN RAISES {}= VAR t1, t2: M3AST_SM.TYPE_SPEC_UNSET; BEGIN M3CTypesMisc.GetTYPE_SPECFromM3TYPE(m1, t1); M3CTypesMisc.GetTYPE_SPECFromM3TYPE(m2, t2); RETURN Identical(t1, t2); END IdenticalM3TYPEs;
We write T <: U to indicate that T is a subtype of U and U is a supertype of T.
If T <: U, then every value of type T is also a value of type U. The converse does not hold: for example, a record or array type with packed fields contains the same values as the corresponding type with unpacked fields, but there is no subtype relation between them. The subtype relation is defined by the following rules:
[n..m] <: INTEGER if n and m are integers [n..m] <: E if n and m are from the enumeration type E [n..m] <: [n'..m'] if [n..m] is a (possibly empty) subset of [n'..m']
ARRAY U OF T <: ARRAY V OF T if NUMBER(U) = NUMBER(V)
That is, one fixed array type is a subtype of another if the element types are identical and the index types have the same number of elements.
ARRAY I1 OF ... ARRAY In OF T <: (ARRAY OF)n T
if the I's are ordinal types or omitted. That is, any n-dimensional array type with element type T is a subtype of (ARRAY OF)n T. (Omitted I's create open array types. They must precede the fixed array types.)
SET OF T <: SET OF T' if T <: T'
NULL <: REF T <: REFANY NULL <: UNTRACED REF T <: ADDRESS
That is, fixed references are subtypes of open references, as long as they are of the same reference class. NIL is a member of every reference type.
NULL <: PROCEDURE(A): R RAISES S for any A, R, and S.
That is, NIL is a member of every procedure type.
PROCEDURE(A): Q RAISES E <: PROCEDURE(B): R RAISES F
if (A): Q RAISES E is covered by (B): R RAISES F. That is, for procedure types, T <: T' if the raises set for T is contained in the raises set for T' and the signatures are otherwise identical except possibly for parameter names and defaults.
OBJECT ... END <: REFANY UNTRACED OBJECT ... END <: ADDRESS
That is, every object is a reference.
NULL <: T OBJECT ... END <: T
That is, NIL is a member of every object subtype (and therefore every object type). Every subtype is included in its supertype.
BITS n FOR T <: T and T <: BITS n FOR T
That is, BITS FOR T has the same values as T.
T <: T for all T T <: U and U <: V implies T <: V for all T, U, V.
That is, <: is reflexive and transitive.
Note that T <: U and U <: T does not imply that T and U are identical, since the subtype relation is unaffected by parameter names, default values, and packing.
PROCEDUREA type T is assignable to a type U if:SameNumber ( ordinal1, ordinal2: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= VAR ns1, ns2: M3CBackEnd.NumStatus; number1, number2: M3AST_SM.Exp_value; BEGIN (* arguments must be ordinals *) IF M3COrdinal.Identical(ordinal1, ordinal2) THEN RETURN TRUE; ELSE ns1 := M3CExpValue.Number(ordinal1, number1); ns2 := M3CExpValue.Number(ordinal2, number2); IF ns1 = ns2 THEN RETURN ns1 # M3CBackEnd.NumStatus.Valid OR M3CBackEnd.Compare(number1, number2) = 0; ELSE (* We return TRUE if one result was unknown and the other was valid *) RETURN ns1 # M3CBackEnd.NumStatus.Overflow AND ns1 # M3CBackEnd.NumStatus.Overflow; END; (* if *) END; (* if *) END SameNumber; PROCEDUREIdenticalElements ( a1, a2: M3AST_AS.Array_type; VAR elemType1, elemType2: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= BEGIN M3CTypesMisc.GetTYPE_SPECFromM3TYPE(a1.as_elementtype, elemType1); M3CTypesMisc.GetTYPE_SPECFromM3TYPE(a2.as_elementtype, elemType2); RETURN Identical(elemType1, elemType2); END IdenticalElements; PROCEDURESubArray (array1, array2: M3AST_AS.Array_type): BOOLEAN RAISES {}= VAR a1, a2: M3AST_AS.Array_type; index1, index2: M3CTypesMisc.Ix; indexType1, indexType2, elemType1, elemType2: M3AST_SM.TYPE_SPEC_UNSET; BEGIN a1 := array1.sm_norm_type; a2 := array2.sm_norm_type; index2 := M3CTypesMisc.Index(a2, indexType2); IF index2 = M3CTypesMisc.Ix.Open THEN LOOP IF IdenticalElements(a1, a2, elemType1, elemType2) THEN RETURN TRUE; ELSE IF M3CTypesMisc.IsOpenArray(elemType2) AND (ISTYPE(elemType1, M3AST_AS.Array_type)) THEN a1 := NARROW(elemType1, M3AST_AS.Array_type).sm_norm_type; a2 := NARROW(elemType2, M3AST_AS.Array_type).sm_norm_type; (* loop *) ELSE RETURN FALSE; END; (* if *) END; (* if *) END; (* loop *) ELSE IF IdenticalElements(a1, a2, elemType1, elemType2) THEN index1 := M3CTypesMisc.Index(a1, indexType1); IF (index1 = M3CTypesMisc.Ix.Ordinal) AND (index2 = M3CTypesMisc.Ix.Ordinal) THEN RETURN SameNumber(indexType1, indexType2); ELSE RETURN (index1 # M3CTypesMisc.Ix.Open) AND Identical(indexType1, indexType2); END; ELSE RETURN FALSE; END; (* if *) END; (* if *) END SubArray; PROCEDUREIdenticalResultTypes ( r1, r2: M3AST_AS.M3TYPE_NULL) : BOOLEAN RAISES {}= BEGIN IF r1 = r2 THEN RETURN TRUE; ELSIF r1 = NIL OR r2 = NIL THEN RETURN FALSE; ELSE RETURN IdenticalM3TYPEs(r1, r2); END; (* if *) END IdenticalResultTypes; PROCEDUREFirstFormal ( id: M3AST_AS.FORMAL_ID; VAR ts: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= BEGIN IF ISTYPE(id, M3AST_AS.F_Value_id) THEN ts := M3CTypesMisc.Concrete(id.sm_type_spec); RETURN TRUE; ELSE RETURN FALSE; END; END FirstFormal; PROCEDUREInternalCovered ( p1, p2: M3AST_AS.Procedure_type; o2: M3AST_AS.Object_type) : BOOLEAN RAISES {}= VAR i1: M3ASTNext.IterFormal; i2: M3ASTNext.IterFormal; formal1, formal2: M3AST_AS.Formal_param; b1, b2: BOOLEAN; id1, id2: M3AST_AS.FORMAL_ID; t1, t2: M3AST_SM.TYPE_SPEC_UNSET; BEGIN (* Check result types identical, 'p1' raises set subset of 'p2's *) IF NOT (IdenticalResultTypes(p1.as_result_type, p2.as_result_type) AND (M3CRaisesSet.Compare(p1.as_raises, p2.as_raises) IN M3CRaisesSet.SubSet)) THEN RETURN FALSE; END; (* Initialize parameter iterations *) i1 := M3ASTNext.NewIterFormal(p1.as_formal_param_s); i2 := M3ASTNext.NewIterFormal(p2.as_formal_param_s); (* Check first parameter if either 'p1' or 'p2' is a method procedure type with an implied first parameter *) b1 := M3CTypesMisc.HiddenObjectParameter(p1, t1); IF o2 # NIL THEN b2 := TRUE; t2 := o2; ELSE b2 := M3CTypesMisc.HiddenObjectParameter(p2, t2); END; IF b1 OR b2 THEN IF NOT (b1 AND b2) THEN IF b1 THEN b2 := M3ASTNext.Formal(i2, formal2, id2); IF NOT (b2 AND FirstFormal(id2, t2)) THEN RETURN FALSE; END; ELSE b1 := M3ASTNext.Formal(i1, formal1, id1); IF NOT (b1 AND FirstFormal(id1, t1)) THEN RETURN FALSE; END; END; END; IF o2 # NIL THEN (* checking for method satisfaction, not normal procedure covering *) IF NOT SubType(t2, t1) THEN RETURN FALSE END; ELSE IF NOT Identical(t1, t2) THEN RETURN FALSE END; END; END; (* Check remaining parameters *) LOOP b1 := M3ASTNext.Formal(i1, formal1, id1); IF b1 # M3ASTNext.Formal(i2, formal2, id2) THEN RETURN FALSE END; IF NOT b1 THEN RETURN TRUE END; (* all parameters checked *) IF (TYPECODE(id1) # TYPECODE(id2)) OR NOT Identical(id1.sm_type_spec, id2.sm_type_spec) THEN RETURN FALSE; END; END; (* loop *) END InternalCovered; <*INLINE*> PROCEDURECovered (p1, p2: M3AST_AS.Procedure_type): BOOLEAN RAISES {}= BEGIN RETURN InternalCovered(p1, p2, NIL); END Covered; PROCEDUREIsAncestor ( ancestor: M3AST_SM.TYPE_SPEC_UNSET; (* must be object or opaque type *) child: M3AST_AS.Object_type) : BOOLEAN RAISES {}= VAR super: M3AST_SM.TYPE_SPEC_UNSET; BEGIN (* note: we already know 'child' and 'ancestor' are not identical *) super := child; WHILE M3ASTNext.SimpleSuperType(super, super) DO LOOP IF Identical(super, ancestor) THEN RETURN TRUE END; IF ISTYPE(super, M3AST_AS.Opaque_type) THEN super := M3CConcTypeSpec.CurrentReveal(super); ELSE EXIT; END; END; IF NOT ISTYPE(super, M3AST_AS.Object_type) THEN RETURN FALSE END; END; (* while *) RETURN FALSE; END IsAncestor; PROCEDUREObjectSubType ( o: M3AST_AS.Object_type; type: M3AST_AS.TYPE_SPEC) : BOOLEAN RAISES {}= VAR traced: BOOLEAN; BEGIN IF ISTYPE(type, M3AST_AS.Object_type) OR ISTYPE(type, M3AST_AS.Opaque_type) THEN RETURN IsAncestor(type, o); ELSE traced := M3CTypesMisc.IsTracedObject(o) IN M3CTypesMisc.ProbablyTraced; TYPECASE type OF | M3AST_AS.RefAny_type => RETURN traced; | M3AST_AS.Address_type => RETURN NOT traced; | M3AST_AS.Root_type(root) => RETURN (root.as_trace_mode = NIL) = traced; ELSE RETURN FALSE; END; END; END ObjectSubType; PROCEDURESubType (type1, type2: M3AST_SM.TYPE_SPEC_UNSET): BOOLEAN RAISES {}= BEGIN IF type1 = type2 OR type1 = NIL OR type2 = NIL THEN RETURN TRUE END; (* assert: neither type is NIL *) type1 := M3CTypesMisc.CheckedUnpack(type1); type2 := M3CTypesMisc.CheckedUnpack(type2); IF type1 = type2 OR type1 = NIL OR type2 = NIL THEN RETURN TRUE END; (* assert: neither type is packed or NIL *) TYPECASE type1 OF | M3AST_AS.Integer_type => IF ISTYPE(type2, M3AST_AS.Integer_type) THEN RETURN TRUE END; RETURN M3COrdinal.SubType(type1, type2); | M3AST_AS.Longint_type => IF ISTYPE(type2, M3AST_AS.Longint_type) THEN RETURN TRUE END; RETURN M3COrdinal.SubType(type1, type2); | M3AST_AS.WideChar_type => IF ISTYPE(type2, M3AST_AS.WideChar_type) THEN RETURN TRUE END; RETURN M3COrdinal.SubType(type1, type2); | M3AST_AS.Enumeration_type, M3AST_AS.Subrange_type, M3AST_AS.Set_type => RETURN M3COrdinal.SubType(type1, type2); ELSE (* drop through *) END; (* case *) (* assert: neither type is packed or unset and 'type1' is not an ordinal or a set *) IF TYPECODE(type1) = TYPECODE(type2) THEN (* assert: types are not unset or packed, are of the same basic form and are not ordinal types, sets or opened opaque types*) TYPECASE type1 OF | M3AST_AS.FLOAT_TYPE, M3AST_AS.RefAny_type, M3AST_AS.Address_type, M3AST_AS.Null_type => RETURN TRUE; | M3AST_AS.Root_type(root_type) => RETURN root_type.as_trace_mode # NIL = (NARROW(type2, M3AST_AS.Root_type).as_trace_mode # NIL); | M3AST_AS.Array_type => RETURN SubArray(type1, type2); | M3AST_AS.Procedure_type => RETURN Covered(type1, type2); | M3AST_AS.Object_type => RETURN Identical(type1, type2) OR IsAncestor(type2, type1); | M3AST_AS.Opaque_type => RETURN SubType(M3CConcTypeSpec.CurrentReveal(type1), type2); ELSE RETURN Identical(type1, type2); END; (* case *) ELSE (* assert: types are not unset or packed, they do not have the same basic form, are not opened opaque types and 'type1' is not ordinal or a set *) TYPECASE type1 OF | M3AST_AS.Null_type => TYPECASE type2 OF | M3AST_AS.Ref_type, M3AST_AS.Procedure_type, M3AST_AS.Object_type, M3AST_AS.Opaque_type, M3AST_AS.Address_type, M3AST_AS.RefAny_type, M3AST_AS.Root_type => RETURN TRUE; ELSE RETURN FALSE; END; (* case *) | M3AST_AS.Ref_type, M3AST_AS.Root_type => IF M3CTypesMisc.IsTracedRef(type1) IN M3CTypesMisc.ProbablyTraced THEN RETURN ISTYPE(type2, M3AST_AS.RefAny_type); ELSE RETURN ISTYPE(type2, M3AST_AS.Address_type); END; | M3AST_AS.Object_type => RETURN ObjectSubType(type1, type2); | M3AST_AS.Opaque_type => RETURN SubType(M3CConcTypeSpec.CurrentReveal(type1), type2); ELSE RETURN FALSE; END; (* case *) END; (* if *) END SubType;
- T <: U, or
- T is a reference type other than ADDRESS(This restriction is lifted in unsafe modules.) and U <: T, or
- T and U are ordinal types with at least one member in common.
PROCEDUREAssignable ( lhs, rhs: M3AST_SM.TYPE_SPEC_UNSET; safe: BOOLEAN) : BOOLEAN RAISES {}= BEGIN IF SubType(rhs, lhs) THEN (* returns TRUE if either 'rhs' or 'lhs' is unset *) RETURN TRUE; ELSE IF (M3CTypesMisc.IsRef(rhs) AND (NOT safe OR NOT ISTYPE(rhs, M3AST_AS.Address_type))) OR ISTYPE(rhs, M3AST_AS.Array_type) THEN RETURN SubType(lhs, rhs); ELSE RETURN M3COrdinal.Overlap(rhs, lhs); END; (* if *) END; (* if *) END Assignable; PROCEDUREVARPassable ( formal, actual: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= BEGIN IF Identical(formal, actual) OR (M3CTypesMisc.IsOpenArray(formal) AND Assignable(formal, actual, TRUE)) OR (M3CTypesMisc.IsOpenArray(actual) AND Assignable(actual, formal, TRUE)) THEN (* type checking is ok - any restrictions imposed by the back end? *) RETURN formal = NIL OR M3CBackEnd.VarParamOK(formal); ELSE RETURN FALSE; END; (* if *) END VARPassable; PROCEDURESatisfies ( procedure: M3AST_AS.Procedure_type; object: M3AST_AS.Object_type; method: M3AST_AS.Procedure_type) : BOOLEAN RAISES {}= BEGIN RETURN InternalCovered(procedure, method, object); END Satisfies; PROCEDURESameReferenceClass ( t1, t2: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= CONST TracedAndUntraced = M3CTypesMisc.RefSet{ M3CTypesMisc.Ref.Traced, M3CTypesMisc.Ref.Untraced}; VAR te1, te2: M3CTypesMisc.Ref; BEGIN te1 := M3CTypesMisc.IsTracedRef(t1); te2 := M3CTypesMisc.IsTracedRef(t2); IF (te1 IN TracedAndUntraced) AND (te2 IN TracedAndUntraced) THEN RETURN te1 = te2; ELSE RETURN (te1 # M3CTypesMisc.Ref.Not) AND (te2 # M3CTypesMisc.Ref.Not); END; (* if *) END SameReferenceClass; <*INLINE*> PROCEDURESameOrdinalSupertype ( type1, type2: M3AST_SM.TYPE_SPEC_UNSET) : BOOLEAN RAISES {}= BEGIN RETURN M3COrdinal.SameSupertype(type1, type2); END SameOrdinalSupertype; BEGIN END M3CTypeRelation.