Example #1
0
 def isSubclassOf(self, other):
     if self is other or self is builtins.getNothingClass():
         return True
     elif other is builtins.getNothingClass():
         return False
     else:
         return other in self.superclasses()
Example #2
0
 def substituteForBase(self, base):
     assert base is not builtins.getNothingClass()
     assert self.clas is not builtins.getNothingClass()
     if self.clas is base:
         return self
     baseType = next(sty for sty in self.clas.supertypes if sty.clas is base)
     return baseType.substitute(self.clas.typeParameters, self.typeArguments)
Example #3
0
 def substituteForBaseClass(self, base):
     assert base is not builtins.getNothingClass()
     assert self.clas is not builtins.getNothingClass()
     supertypePath = self.clas.findTypePathToBaseClass(base)
     ty = self
     for sty in supertypePath:
         ty = sty.substitute(ty.clas.typeParameters, ty.typeArguments)
     return ty
Example #4
0
 def addTypeDefn(irTypeDefn):
     subtypeGraph.addVertex(irTypeDefn.id)
     if isinstance(irTypeDefn, ir.ObjectTypeDefn):
         for supertype in irTypeDefn.supertypes:
             if supertype.isNullable():
                 raise InheritanceException.fromDefn(irTypeDefn,
                                                     "cannot inherit nullable type")
             if supertype.clas is getNothingClass():
                 raise InheritanceException.fromDefn(irTypeDefn, "cannot inherit Nothing")
             supertypeId = getIdForType(supertype)
             if irTypeDefn.id is supertypeId:
                 raise InheritanceException.fromDefn(irTypeDefn,
                                                     "cannot inherit from itself")
             if not supertype.clas.isLocal():
                 NonLocalObjectTypeDefnScope.ensureForDefn(supertype.clas, info)
             subtypeGraph.addEdge(irTypeDefn.id, supertypeId)
     elif isinstance(irTypeDefn, ir.TypeParameter):
         upperBoundId = getIdForType(irTypeDefn.upperBound)
         if irTypeDefn.id is upperBoundId:
             raise InheritanceException.fromDefn(irTypeDefn,
                                                 "cannot be upper bounded by itself")
         subtypeGraph.addEdge(irTypeDefn.id, upperBoundId)
         lowerBoundId = getIdForType(irTypeDefn.lowerBound)
         if irTypeDefn.id is lowerBoundId:
             raise InheritanceException.fromDefn(irTypeDefn,
                                                 "cannot be lower bounded by itself")
         subtypeGraph.addEdge(lowerBoundId, irTypeDefn.id)
     else:
         raise NotImplementedError()
Example #5
0
 def getReturnType(self):
     if self.declaredReturnType:
         return self.declaredReturnType
     elif self.bodyReturnType:
         return self.bodyReturnType
     else:
         return ir_t.ClassType.forReceiver(getNothingClass())
Example #6
0
    def glb_(self, ty, stack):
        if (self, ty) in stack:
            if self.isObject() and ty.isObject():
                return getNothingClassType()
            else:
                return NoType

        if self == ty:
            return self
        elif self is NoType:
            return ty
        elif ty is NoType:
            return self
        elif self.isSubtypeOf(ty):
            return self
        elif ty.isSubtypeOf(self):
            return ty
        elif self.isObject() and ty.isObject():
            # Ok, this is kind of a cop-out. Don't judge me.
            # TODO: once there are intersection types, use them instead.
            if self.isNullable() and ty.isNullable():
                flags = frozenset([NULLABLE_TYPE_FLAG])
            else:
                flags = frozenset()
            return ClassType(builtins.getNothingClass(), (), flags)
        else:
            return NoType
Example #7
0
 def addTypeDefn(irTypeDefn):
     subtypeGraph.addVertex(irTypeDefn.id)
     if isinstance(irTypeDefn, ir.ObjectTypeDefn):
         for supertype in irTypeDefn.supertypes:
             if supertype.isNullable():
                 raise InheritanceException.fromDefn(
                     irTypeDefn, "cannot inherit nullable type")
             if supertype.clas is getNothingClass():
                 raise InheritanceException.fromDefn(
                     irTypeDefn, "cannot inherit Nothing")
             supertypeId = getIdForType(supertype)
             if irTypeDefn.id is supertypeId:
                 raise InheritanceException.fromDefn(
                     irTypeDefn, "cannot inherit from itself")
             if not supertype.clas.isLocal():
                 NonLocalObjectTypeDefnScope.ensureForDefn(
                     supertype.clas, info)
             subtypeGraph.addEdge(irTypeDefn.id, supertypeId)
     elif isinstance(irTypeDefn, ir.TypeParameter):
         upperBoundId = getIdForType(irTypeDefn.upperBound)
         if irTypeDefn.id is upperBoundId:
             raise InheritanceException.fromDefn(
                 irTypeDefn, "cannot be upper bounded by itself")
         subtypeGraph.addEdge(irTypeDefn.id, upperBoundId)
         lowerBoundId = getIdForType(irTypeDefn.lowerBound)
         if irTypeDefn.id is lowerBoundId:
             raise InheritanceException.fromDefn(
                 irTypeDefn, "cannot be lower bounded by itself")
         subtypeGraph.addEdge(lowerBoundId, irTypeDefn.id)
     else:
         raise NotImplementedError()
Example #8
0
    def findTypePathToBaseClass(self, base):
        """Returns a list of supertypes (ClassTypes), which represent a path through the class
        DAG from this class to the given base class. The path does not include a type for this
        class, but it does include the supertype for the base. If the given class is not a
        base, returns None. This class must not be Nothing, since there is no well-defined
        path in that case."""
        assert self is not builtins.getNothingClass()
        path = []
        indexStack = [0]
        assert self.id is not None
        visited = set([self.id])

        while len(indexStack) > 0:
            index = indexStack[-1]
            indexStack[-1] += 1
            clas = path[-1].clas if len(path) > 0 else self
            if clas is base:
                return path
            elif index == len(clas.supertypes):
                if len(path) > 0:
                    path.pop()
                indexStack.pop()
            elif clas.supertypes[index].clas.id not in visited:
                supertype = clas.supertypes[index]
                assert supertype.clas.id is not None
                visited.add(supertype.clas.id)
                path.append(supertype)
                indexStack.append(0)
        return None
Example #9
0
 def superclasses(self):
     """Returns a generator of superclasses in depth-first order, including this class."""
     assert self is not builtins.getNothingClass()
     yield self
     clas = self
     while len(clas.supertypes) > 0:
         clas = clas.supertypes[0].clas
         yield clas
Example #10
0
    def findCommonBaseClass(self, other):
        """Returns a class which (a) is a superclass of both classes, and (b) has no subclasses
        which are superclasses of both classes."""
        if self is other:
            return self
        if self is builtins.getNothingClass():
            return other
        if other is builtins.getNothingClass():
            return self

        selfBases = list(self.superclasses())
        otherBases = list(other.superclasses())
        if selfBases[-1] is not otherBases[-1]:
            return None

        selfLast = -len(selfBases) - 1
        otherLast = -len(otherBases) - 1
        i = -1
        while i > selfLast and i > otherLast and selfBases[i] is otherBases[i]:
            i -= 1
        return selfBases[i + 1]
Example #11
0
 def superclass(self):
     assert self is not builtins.getNothingClass()
     if len(self.supertypes) == 0:
         return None
     else:
         return self.supertypes[0].clas
Example #12
0
    def isSubtypeOfRules_(self, other, subEnv):
        left = self
        right = other

        # Basic rules that apply to all types.
        if left.isEquivalent(right):
            return True
        if left is AnyType or right is NoType:
            return False
        if right is AnyType or left is NoType:
            return True

        # Primitive types.
        if left.isPrimitive() or right.isPrimitive():
            return False
        assert left.isObject() and right.isObject()

        # Existential types.
        if isinstance(right, ExistentialType):
            utils.each(subEnv.addVariable, right.variables)
            return left.isSubtypeOf_(right.ty, subEnv)
        if subEnv.isExistentialVar(right):
            return subEnv.trySubstitute(left, right)
        if isinstance(left, ExistentialType):
            return left.ty.isSubtypeOf_(right, subEnv)

        # A nullable type cannot be a subtype of a non-nullable type. Note that existential
        # types cannot be nullable (but their inner types can).
        if left.isNullable() and not right.isNullable():
            return False

        # Variable types.
        if isinstance(left, VariableType) and isinstance(right, VariableType):
            # If both are variable types, try to find a common bound.
            leftBound = left
            rightFirstBound = right
            while isinstance(leftBound, VariableType):
                rightBound = rightFirstBound
                while isinstance(rightBound, VariableType):
                    if leftBound.typeParameter is rightBound.typeParameter and \
                       (rightBound.isNullable() or not leftBound.isNullable()):
                        return True
                    rightBound = rightBound.lowerBound()
                leftBound = leftBound.upperBound()
        while isinstance(left, VariableType):
            left = left.upperBound()
        while isinstance(right, VariableType):
            right = right.lowerBound()

        # Class types.
        assert isinstance(left, ClassType) and isinstance(right, ClassType)

        # Special cases for `Nothing`.
        if left.clas is builtins.getNothingClass():
            return True
        if right.clas is builtins.getNothingClass():
            return False

        # Check that left is derived from right, and substitute for the same definition.
        leftBase = left.clas.findBaseType(right.clas)
        if leftBase is None:
            return False
        left = leftBase.substitute(left.clas.typeParameters, left.typeArguments)

        # Compare type arguments, based on variance.
        for lty, rty, tp in \
                zip(left.typeArguments, right.typeArguments, left.clas.typeParameters):
            # This could be more compact, but it's better for debugging to have the cases split.
            variance = tp.variance()
            if variance is flags.COVARIANT:
                if not lty.isSubtypeOf_(rty, subEnv):
                    return False
            elif variance is flags.CONTRAVARIANT:
                if not rty.isSubtypeOf_(lty, subEnv):
                    return False
            else:
                assert variance is INVARIANT
                if not (subEnv.isExistentialVar(rty) and subEnv.trySubstitute(lty, rty)) and \
                   not lty.isEquivalent(rty):
                    return False
        return True
Example #13
0
    def _lubRec(self, other, stack):
        # We need to be able to detect infinite recursion in order to ensure termination.
        # Consider the case below:
        #   class A[+T]
        #   class B <: A[B]
        #   class C <: A[C]
        # Suppose we want to find B lub C.
        # The correct answer is A[B lub C] = A[A[B lub C]] = A[A[A[B lub C]]] ...
        # Since we have no way to correctly express the least upper bound in that case, we
        # settle for returning a close upper bound: A[Object].

        if (self, other) in stack:
            if self.isObject() and other.isObject():
                return getRootClassType()
            else:
                return AnyType

        # Basic rules that apply to all types.
        if self == other:
            return self
        if self is AnyType or other is AnyType:
            return self
        if self is NoType:
            return other
        if other is NoType:
            return self

        # Rules below apply only to object types.
        if self.isObject() and other.isObject():
            # If either side is nullable, the result is nullable.
            if self.isNullable() or other.isNullable():
                combinedFlags = frozenset([NULLABLE_TYPE_FLAG])
            else:
                combinedFlags = frozenset()

            # If both types are variables with a common variable bound, return that.
            if isinstance(self, VariableType) and isinstance(other, VariableType):
                sharedBound = self.typeParameter.findCommonUpperBound(other.typeParameter)
                if sharedBound is not None:
                    return VariableType(sharedBound, combinedFlags)

            # If either type is Nothing, return the other one.
            if isinstance(self, ClassType) and self.clas is builtins.getNothingClass():
                return other.withFlags(combinedFlags)
            if isinstance(other, ClassType) and other.clas is builtins.getNothingClass():
                return self.withFlags(combinedFlags)

            # Since there is no common bound, the result will be a class type, so we peel back
            # the bounds until both sides are class types.
            left = self
            while isinstance(left, VariableType):
                left = left.typeParameter.upperBound
            right = other
            while isinstance(right, VariableType):
                right = right.typeParameter.upperBound
            assert isinstance(left, ClassType) and isinstance(right, ClassType)

            # Find a common base class. We don't assume that there is a single root class
            # (even though there is), so this can fail.
            baseClass = left.clas.findCommonBaseClass(right.clas)
            while baseClass is not None:
                left = left.substituteForBaseClass(baseClass)
                right = right.substituteForBaseClass(baseClass)

                # We need to combine the type arguments, according to the variance of the
                # corresponding type parameters. This is not necessarily possible. If we get
                # stuck, we'll try again with the superclass.
                leftArgs = left.substituteForBaseClass(baseClass).typeArguments
                rightArgs = right.substituteForBaseClass(baseClass).typeArguments

                combinedArgs = []
                combineSuccess = True
                for param, leftArg, rightArg in zip(baseClass.typeParameters,
                                                    leftArgs, rightArgs):
                    variance = param.variance()
                    if variance is INVARIANT:
                        if leftArg == rightArg:
                            combined = leftArg
                        else:
                            combined = AnyType
                    else:
                        stack.append((self, other))
                        if variance is flags.COVARIANT:
                            combined = leftArg._lubRec(rightArg, stack)
                        else:
                            assert variance is flags.CONTRAVARIANT
                            combined = leftArg._glbRec(rightArg, stack)
                        stack.pop()
                    if combined is AnyType:
                        combineSuccess = False
                        break
                    combinedArgs.append(combined)

                if combineSuccess:
                    return ClassType(baseClass, tuple(combinedArgs), combinedFlags)

                baseClass = baseClass.superclass()

            # If we get here, then we ran out of superclasses. Fall through.

        return AnyType
Example #14
0
    def lubRec_(self, other, stack):
        # We need to be able to detect infinite recursion in order to ensure termination.
        # Consider the case below:
        #   class A[+T]
        #   class B <: A[B]
        #   class C <: A[C]
        # Suppose we want to find B lub C.
        # The correct answer is A[B lub C] = A[A[B lub C]] = A[A[A[B lub C]]] ...
        # Since we have no way to correctly express the least upper bound in that case, we
        # settle for returning a close upper bound: A[Object].

        if (self, other) in stack:
            if self.isObject() and other.isObject():
                return getRootClassType()
            else:
                return AnyType

        # Basic rules that apply to all types.
        if self.isEquivalent(other):
            return self
        if self is AnyType or other is AnyType:
            return self
        if self is NoType:
            return other
        if other is NoType:
            return self

        # Rules for existential types (always recursive)
        if isinstance(self, ExistentialType) or isinstance(other, ExistentialType):
            stack.append((self, other))

            # At this point, we consider both types to be existential. Note that any type
            # can be trivially closed with an existential. For example:
            #   String == forsome [X] String

            # We get the lub of the inner types.
            selfInnerType = self.ty if isinstance(self, ExistentialType) else self
            otherInnerType = other.ty if isinstance(other, ExistentialType) else other
            innerLubType = selfInnerType.lubRec_(otherInnerType, stack)
            stack.pop()

            # Find which variables are still being used in the lub. If some of the variables
            # from `self` and `other` are still present, return an existential with those.
            # Note that unused variables can be removed from an existential type. For example:
            #   forsome [X] Option[String] == Option[String]
            # We must be careful the returned type has variables in a deterministic order.
            # Technically, the order shouldn't matter, but it's infeasible to test whether two
            # types are equivalent with arbitrary ordered variables, since they cannot easily
            # be sorted.
            selfVariables = self.variables if isinstance(self, ExistentialType) else ()
            otherVariables = other.variables if isinstance(other, ExistentialType) else ()
            return ExistentialType.close(selfVariables + otherVariables, innerLubType)

        # Rules below apply only to object types.
        if self.isObject() and other.isObject():
            # If either side is nullable, the result is nullable.
            if self.isNullable() or other.isNullable():
                combinedFlags = frozenset([NULLABLE_TYPE_FLAG])
            else:
                combinedFlags = frozenset()

            # If both types are variables with a common variable bound, return that.
            if isinstance(self, VariableType) and isinstance(other, VariableType):
                sharedBound = self.typeParameter.findCommonUpperBound(other.typeParameter)
                if sharedBound is not None:
                    return VariableType(sharedBound, combinedFlags)

            # If either type is Nothing, return the other one.
            if isinstance(self, ClassType) and self.clas is builtins.getNothingClass():
                return other.withFlags(combinedFlags)
            if isinstance(other, ClassType) and other.clas is builtins.getNothingClass():
                return self.withFlags(combinedFlags)

            # Since there is no common bound, the result will be a class type, so we peel back
            # the bounds until both sides are class types.
            left = self
            while isinstance(left, VariableType):
                left = left.typeParameter.upperBound
            right = other
            while isinstance(right, VariableType):
                right = right.typeParameter.upperBound
            assert isinstance(left, ClassType) and isinstance(right, ClassType)

            # Find a common base class. We don't assume that there is a single root class
            # (even though there is), so this can fail.
            baseClass = left.clas.findCommonBaseClass(right.clas)
            while baseClass is not None:
                left = left.substituteForBaseClass(baseClass)
                right = right.substituteForBaseClass(baseClass)

                # We need to combine the type arguments, according to the variance of the
                # corresponding type parameters. This is not necessarily possible. If we get
                # stuck, we'll try again with the superclass.
                leftArgs = left.typeArguments
                rightArgs = right.typeArguments

                combinedArgs = []
                combineSuccess = True
                for param, leftArg, rightArg in zip(baseClass.typeParameters,
                                                    leftArgs, rightArgs):
                    variance = param.variance()
                    if variance is INVARIANT:
                        if leftArg == rightArg:
                            combined = leftArg
                        else:
                            combined = AnyType
                    else:
                        stack.append((self, other))
                        if variance is flags.COVARIANT:
                            combined = leftArg.lubRec_(rightArg, stack)
                        else:
                            assert variance is flags.CONTRAVARIANT
                            combined = leftArg.glbRec_(rightArg, stack)
                        stack.pop()
                    if combined is AnyType:
                        combineSuccess = False
                        break
                    combinedArgs.append(combined)

                if combineSuccess:
                    return ClassType(baseClass, tuple(combinedArgs), combinedFlags)

                baseClass = baseClass.superclass()

            # If we get here, then we ran out of superclasses. Fall through.

        return AnyType
Example #15
0
 def testFindCommonBaseClassWithNothing(self):
     nothing = getNothingClass()
     self.assertIs(self.A, self.A.findCommonBaseClass(nothing))
     self.assertIs(self.A, nothing.findCommonBaseClass(self.A))
Example #16
0
def getNothingClassType():
    return ClassType(builtins.getNothingClass())
Example #17
0
 def testThrowExpr(self):
     info = self.analyzeFromSource("def f(exn: Exception) = throw exn")
     self.assertEquals(ClassType(getNothingClass()),
                       info.package.findFunction(name="f").returnType)
     self.assertEquals(NoType, info.getType(info.ast.definitions[0].body))
Example #18
0
def getNullType():
    return ClassType(builtins.getNothingClass(), (), frozenset([NULLABLE_TYPE_FLAG]))
Example #19
0
 def testFindCommonBaseClassWithNothing(self):
     nothing = builtins.getNothingClass()
     self.assertIs(self.A, self.A.findCommonBaseClass(nothing))
     self.assertIs(self.A, nothing.findCommonBaseClass(self.A))