예제 #1
0
 def test_type_parameter_equality(self):
   param1 = abstract.TypeParameter("S", self._ctx)
   param2 = abstract.TypeParameter("T", self._ctx)
   cls = abstract.InterpreterClass("S", [], {}, None, self._ctx)
   self.assertEqual(param1, param1)
   self.assertNotEqual(param1, param2)
   self.assertNotEqual(param1, cls)
예제 #2
0
 def test_type_parameter_official_name(self):
   param = abstract.TypeParameter("T", self._ctx)
   self._ctx.vm.frame = frame_state.SimpleFrame()  # for error logging
   param.update_official_name("T")
   self.assertFalse(self._ctx.errorlog.has_error())
   param.update_official_name("Q")
   self.assertTrue(self._ctx.errorlog.has_error())
예제 #3
0
 def test_type_parameter_instance_bad_attribute(self):
   t = abstract.TypeParameter(abstract_utils.T, self._ctx)
   t_instance = abstract.TypeParameterInstance(
       t, self._ctx.convert.primitive_class_instances[str], self._ctx)
   node, var = self._ctx.attribute_handler.get_attribute(
       self._ctx.root_node, t_instance, "rumpelstiltskin")
   self.assertIs(node, self._ctx.root_node)
   self.assertIsNone(var)
예제 #4
0
 def test_instantiate_type_parameter_type(self):
   params = {
       abstract_utils.T: abstract.TypeParameter(abstract_utils.T, self._ctx)
   }
   cls = abstract.ParameterizedClass(self._ctx.convert.type_type, params,
                                     self._ctx)
   self.assertListEqual(
       cls.instantiate(self._node).data, [self._ctx.convert.unsolvable])
예제 #5
0
 def test_type_parameter_instance(self):
   t = abstract.TypeParameter(abstract_utils.T, self._ctx)
   t_instance = abstract.TypeParameterInstance(
       t, self._ctx.convert.primitive_class_instances[str], self._ctx)
   node, var = self._ctx.attribute_handler.get_attribute(
       self._ctx.root_node, t_instance, "upper")
   self.assertIs(node, self._ctx.root_node)
   attr, = var.data
   self.assertIsInstance(attr, abstract.PyTDFunction)
예제 #6
0
 def test_call_with_type_parameter(self):
   ret_cls = abstract.ParameterizedClass(
       self._ctx.convert.list_type,
       {abstract_utils.T: abstract.TypeParameter(abstract_utils.T, self._ctx)},
       self._ctx)
   f = self._make_func(
       param_names=("test",),
       annotations={
           "test": abstract.TypeParameter(abstract_utils.T, self._ctx),
           "return": ret_cls
       })
   args = function.Args(
       posargs=(self._ctx.convert.build_int(self._ctx.root_node),))
   _, ret = f.call(self._ctx.root_node, f, args)
   # ret is an Instance(ParameterizedClass(list, {abstract_utils.T: int}))
   # but we really only care about T.
   self.assertIs(ret.data[0].cls.formal_type_parameters[abstract_utils.T],
                 self._ctx.convert.int_type)
예제 #7
0
 def test_call_empty_type_parameter_instance(self):
   instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
   t = abstract.TypeParameter(abstract_utils.T, self._ctx)
   t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
   node, ret = t_instance.call(self._node, t_instance.to_binding(self._node),
                               function.Args(posargs=()))
   self.assertIs(node, self._node)
   retval, = ret.data
   self.assertIs(retval, self._ctx.convert.empty)
예제 #8
0
 def test_empty_type_parameter_instance(self):
   t = abstract.TypeParameter(
       abstract_utils.T, self._ctx, bound=self._ctx.convert.int_type)
   instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
   t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
   node, var = self._ctx.attribute_handler.get_attribute(
       self._ctx.root_node, t_instance, "real")
   self.assertIs(node, self._ctx.root_node)
   attr, = var.data
   self.assertIs(attr, self._ctx.convert.primitive_class_instances[int])
예제 #9
0
 def test_type_parameter_instance_set_attribute(self):
   t = abstract.TypeParameter(abstract_utils.T, self._ctx)
   t_instance = abstract.TypeParameterInstance(
       t, self._ctx.convert.primitive_class_instances[str], self._ctx)
   node = self._ctx.attribute_handler.set_attribute(
       self._ctx.root_node, t_instance, "rumpelstiltskin",
       self._ctx.new_unsolvable(self._ctx.root_node))
   self.assertIs(node, self._ctx.root_node)
   self.assertEqual(
       str(self._ctx.errorlog).strip(),
       "Can't assign attribute 'rumpelstiltskin' on str [not-writable]")
예제 #10
0
 def test_call_type_parameter_instance(self):
   instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
   instance.merge_instance_type_parameter(
       self._ctx.root_node, abstract_utils.T,
       self._ctx.convert.int_type.to_variable(self._ctx.root_node))
   t = abstract.TypeParameter(abstract_utils.T, self._ctx)
   t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
   node, ret = t_instance.call(self._node, t_instance.to_binding(self._node),
                               function.Args(posargs=()))
   self.assertIs(node, self._node)
   retval, = ret.data
   self.assertEqual(retval.cls, self._ctx.convert.int_type)
예제 #11
0
 def test_call_type_parameter_instance_with_wrong_args(self):
   instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
   instance.merge_instance_type_parameter(
       self._ctx.root_node, abstract_utils.T,
       self._ctx.convert.int_type.to_variable(self._ctx.root_node))
   t = abstract.TypeParameter(abstract_utils.T, self._ctx)
   t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
   posargs = (self._ctx.new_unsolvable(self._node),) * 3
   node, ret = t_instance.call(self._node, t_instance.to_binding(self._node),
                               function.Args(posargs=posargs))
   self.assertIs(node, self._node)
   self.assertTrue(ret.bindings)
   error, = self._ctx.errorlog
   self.assertEqual(error.name, "wrong-arg-count")
예제 #12
0
 def test_instantiate_tuple_class_for_sub(self):
   type_param = abstract.TypeParameter(abstract_utils.K, self._ctx)
   cls = abstract.TupleClass(self._ctx.convert.tuple_type, {
       0: type_param,
       abstract_utils.T: type_param
   }, self._ctx)
   # Instantiate the tuple class.
   subst_value = cls.instantiate(self._ctx.root_node,
                                 abstract_utils.DUMMY_CONTAINER)
   # Recover the class from the instance.
   subbed_cls = self._ctx.annotation_utils.sub_one_annotation(
       self._ctx.root_node, type_param, [{
           abstract_utils.K: subst_value
       }])
   self.assertEqual(cls, subbed_cls)
예제 #13
0
def make_replace_method(ctx, node, cls, *, kwargs_name="kwargs"):
    """Create a replace() method for a dataclass."""
    # This is used by several packages that extend dataclass.
    # The signature is
    #   def replace(self: T, **kwargs) -> T
    typevar = abstract.TypeParameter(abstract_utils.T + cls.name,
                                     ctx,
                                     bound=cls)
    return overlay_utils.make_method(
        ctx=ctx,
        node=node,
        name="replace",
        return_type=typevar,
        self_param=overlay_utils.Param("self", typevar),
        kwargs=overlay_utils.Param(kwargs_name),
    )
예제 #14
0
 def _get_typeparam(self, node, args):
     args = args.simplify(node, self.ctx)
     try:
         self.match_args(node, args)
     except function.InvalidParameters as e:
         raise TypeVarError("wrong arguments", e.bad_call) from e
     except function.FailedFunctionCall as e:
         # It is currently impossible to get here, since the only
         # FailedFunctionCall that is not an InvalidParameters is NotCallable.
         raise TypeVarError("initialization failed") from e
     name = self._get_constant(args.posargs[0],
                               "name",
                               str,
                               arg_type_desc="a constant str")
     constraints = tuple(
         self._get_annotation(node, c, "constraint")
         for c in args.posargs[1:])
     if len(constraints) == 1:
         raise TypeVarError(
             "the number of constraints must be 0 or more than 1")
     bound = self._get_namedarg(node, args, "bound", None)
     covariant = self._get_namedarg(node, args, "covariant", False)
     contravariant = self._get_namedarg(node, args, "contravariant", False)
     if constraints and bound:
         raise TypeVarError(
             "constraints and a bound are mutually exclusive")
     extra_kwargs = set(
         args.namedargs) - {"bound", "covariant", "contravariant"}
     if extra_kwargs:
         raise TypeVarError("extra keyword arguments: " +
                            ", ".join(extra_kwargs))
     if args.starargs:
         raise TypeVarError("*args must be a constant tuple")
     if args.starstarargs:
         raise TypeVarError("ambiguous **kwargs not allowed")
     return abstract.TypeParameter(name,
                                   self.ctx,
                                   constraints=constraints,
                                   bound=bound,
                                   covariant=covariant,
                                   contravariant=contravariant)
예제 #15
0
  def _constant_to_value(self, pyval, subst, get_node):
    """Create a BaseValue that represents a python constant.

    This supports both constant from code constant pools and PyTD constants such
    as classes. This also supports builtin python objects such as int and float.

    Args:
      pyval: The python or PyTD value to convert.
      subst: The current type parameters.
      get_node: A getter function for the current node.

    Returns:
      A Value that represents the constant, or None if we couldn't convert.
    Raises:
      NotImplementedError: If we don't know how to convert a value.
      TypeParameterError: If we can't find a substitution for a type parameter.
    """
    if isinstance(pyval, str):
      return abstract.ConcreteValue(pyval, self.str_type, self.ctx)
    elif isinstance(pyval, bytes):
      return abstract.ConcreteValue(pyval, self.bytes_type, self.ctx)
    elif isinstance(pyval, bool):
      return self.true if pyval else self.false
    elif isinstance(pyval, int) and -1 <= pyval <= _MAX_IMPORT_DEPTH:
      # For small integers, preserve the actual value (for things like the
      # level in IMPORT_NAME).
      return abstract.ConcreteValue(pyval, self.int_type, self.ctx)
    elif pyval.__class__ in self.primitive_classes:
      return self.primitive_class_instances[pyval.__class__]
    elif pyval.__class__ is frozenset:
      instance = abstract.Instance(self.frozenset_type, self.ctx)
      for element in pyval:
        instance.merge_instance_type_parameter(
            self.ctx.root_node, abstract_utils.T,
            self.constant_to_var(element, subst, self.ctx.root_node))
      return instance
    elif isinstance(pyval, (loadmarshal.CodeType, blocks.OrderedCode)):
      return abstract.ConcreteValue(pyval,
                                    self.primitive_classes[types.CodeType],
                                    self.ctx)
    elif pyval is super:
      return special_builtins.Super(self.ctx)
    elif pyval is object:
      return special_builtins.Object(self.ctx)
    elif pyval.__class__ is type:
      try:
        return self.name_to_value(self._type_to_name(pyval), subst)
      except (KeyError, AttributeError):
        log.debug("Failed to find pytd", exc_info=True)
        raise
    elif isinstance(pyval, pytd.LateType):
      actual = self._load_late_type(pyval)
      return self._constant_to_value(actual, subst, get_node)
    elif isinstance(pyval, pytd.TypeDeclUnit):
      return self._create_module(pyval)
    elif isinstance(pyval, pytd.Module):
      mod = self.ctx.loader.import_name(pyval.module_name)
      return self._create_module(mod)
    elif isinstance(pyval, pytd.Class):
      if pyval.name == "builtins.super":
        return self.ctx.special_builtins["super"]
      elif pyval.name == "builtins.object":
        return self.object_type
      elif pyval.name == "types.ModuleType":
        return self.module_type
      elif pyval.name == "_importlib_modulespec.ModuleType":
        # Python 3's typeshed uses a stub file indirection to define ModuleType
        # even though it is exported via types.pyi.
        return self.module_type
      elif pyval.name == "types.FunctionType":
        return self.function_type
      else:
        module, dot, base_name = pyval.name.rpartition(".")
        # typing.TypingContainer intentionally loads the underlying pytd types.
        if (module not in ("typing", "typing_extensions") and
            module in overlay_dict.overlays):
          overlay = self.ctx.vm.import_module(module, module, 0)
          if overlay.get_module(base_name) is overlay:
            overlay.load_lazy_attribute(base_name)
            return abstract_utils.get_atomic_value(overlay.members[base_name])
        try:
          cls = abstract.PyTDClass.make(base_name, pyval, self.ctx)
        except mro.MROError as e:
          self.ctx.errorlog.mro_error(self.ctx.vm.frames, base_name, e.mro_seqs)
          cls = self.unsolvable
        else:
          if dot:
            cls.module = module
          cls.call_metaclass_init(get_node())
        return cls
    elif isinstance(pyval, pytd.Function):
      signatures = [
          abstract.PyTDSignature(pyval.name, sig, self.ctx)
          for sig in pyval.signatures
      ]
      type_new = self.ctx.loader.lookup_builtin("builtins.type").Lookup(
          "__new__")
      if pyval is type_new:
        f_cls = special_builtins.TypeNew
      else:
        f_cls = abstract.PyTDFunction
      f = f_cls(pyval.name, signatures, pyval.kind, self.ctx)
      f.is_abstract = pyval.is_abstract
      return f
    elif isinstance(pyval, pytd.ClassType):
      if pyval.cls:
        cls = pyval.cls
      else:
        # If pyval is a reference to a class in builtins or typing, we can fill
        # in the class ourselves. lookup_builtin raises a KeyError if the name
        # is not found.
        cls = self.ctx.loader.lookup_builtin(pyval.name)
        assert isinstance(cls, pytd.Class)
      return self.constant_to_value(cls, subst, self.ctx.root_node)
    elif isinstance(pyval, pytd.NothingType):
      return self.empty
    elif isinstance(pyval, pytd.AnythingType):
      return self.unsolvable
    elif (isinstance(pyval, pytd.Constant) and
          isinstance(pyval.type, pytd.AnythingType)):
      # We allow "X = ... # type: Any" to declare X as a type.
      return self.unsolvable
    elif (isinstance(pyval, pytd.Constant) and
          isinstance(pyval.type, pytd.GenericType) and
          pyval.type.name == "builtins.type"):
      # `X: Type[other_mod.X]` is equivalent to `X = other_mod.X`.
      param, = pyval.type.parameters
      return self.constant_to_value(param, subst, self.ctx.root_node)
    elif isinstance(pyval, pytd.UnionType):
      options = [
          self.constant_to_value(t, subst, self.ctx.root_node)
          for t in pyval.type_list
      ]
      if len(options) > 1:
        return abstract.Union(options, self.ctx)
      else:
        return options[0]
    elif isinstance(pyval, pytd.TypeParameter):
      constraints = tuple(
          self.constant_to_value(c, {}, self.ctx.root_node)
          for c in pyval.constraints)
      bound = (
          pyval.bound and
          self.constant_to_value(pyval.bound, {}, self.ctx.root_node))
      return abstract.TypeParameter(
          pyval.name,
          self.ctx,
          constraints=constraints,
          bound=bound,
          module=pyval.scope)
    elif isinstance(pyval, abstract_utils.AsInstance):
      cls = pyval.cls
      if isinstance(cls, pytd.LateType):
        actual = self._load_late_type(cls)
        if not isinstance(actual, pytd.ClassType):
          return self.unsolvable
        cls = actual.cls
      if isinstance(cls, pytd.ClassType):
        cls = cls.cls
      if isinstance(cls, pytd.GenericType) and cls.name == "typing.ClassVar":
        param, = cls.parameters
        return self.constant_to_value(
            abstract_utils.AsInstance(param), subst, self.ctx.root_node)
      elif isinstance(cls, pytd.GenericType) or (isinstance(cls, pytd.Class) and
                                                 cls.template):
        # If we're converting a generic Class, need to create a new instance of
        # it. See test_classes.testGenericReinstantiated.
        if isinstance(cls, pytd.Class):
          params = tuple(t.type_param.upper_value for t in cls.template)
          cls = pytd.GenericType(base_type=pytd.ClassType(cls.name, cls),
                                 parameters=params)
        if isinstance(cls.base_type, pytd.LateType):
          actual = self._load_late_type(cls.base_type)
          if not isinstance(actual, pytd.ClassType):
            return self.unsolvable
          base_cls = actual.cls
        else:
          base_type = cls.base_type
          assert isinstance(base_type, pytd.ClassType)
          base_cls = base_type.cls
        assert isinstance(base_cls, pytd.Class), base_cls
        if base_cls.name == "builtins.type":
          c, = cls.parameters
          if isinstance(c, pytd.TypeParameter):
            if not subst or c.full_name not in subst:
              raise self.TypeParameterError(c.full_name)
            # deformalize gets rid of any unexpected TypeVars, which can appear
            # if something is annotated as Type[T].
            return self.ctx.annotation_utils.deformalize(
                self.merge_classes(subst[c.full_name].data))
          else:
            return self.constant_to_value(c, subst, self.ctx.root_node)
        elif isinstance(cls, pytd.TupleType):
          content = tuple(self.constant_to_var(abstract_utils.AsInstance(p),
                                               subst, get_node())
                          for p in cls.parameters)
          return self.tuple_to_value(content)
        elif isinstance(cls, pytd.CallableType):
          clsval = self.constant_to_value(cls, subst, self.ctx.root_node)
          return abstract.Instance(clsval, self.ctx)
        else:
          clsval = self.constant_to_value(base_cls, subst, self.ctx.root_node)
          instance = abstract.Instance(clsval, self.ctx)
          num_params = len(cls.parameters)
          assert num_params <= len(base_cls.template)
          for i, formal in enumerate(base_cls.template):
            if i < num_params:
              node = get_node()
              p = self.constant_to_var(
                  abstract_utils.AsInstance(cls.parameters[i]), subst, node)
            else:
              # An omitted type parameter implies `Any`.
              node = self.ctx.root_node
              p = self.unsolvable.to_variable(node)
            instance.merge_instance_type_parameter(node, formal.name, p)
          return instance
      elif isinstance(cls, pytd.Class):
        assert not cls.template
        # This key is also used in __init__
        key = (abstract.Instance, cls)
        if key not in self._convert_cache:
          if cls.name in ["builtins.type", "builtins.property"]:
            # An instance of "type" or of an anonymous property can be anything.
            instance = self._create_new_unknown_value("type")
          else:
            mycls = self.constant_to_value(cls, subst, self.ctx.root_node)
            instance = abstract.Instance(mycls, self.ctx)
          log.info("New pytd instance for %s: %r", cls.name, instance)
          self._convert_cache[key] = instance
        return self._convert_cache[key]
      elif isinstance(cls, pytd.Literal):
        return self.constant_to_value(
            self._get_literal_value(cls.value), subst, self.ctx.root_node)
      else:
        return self.constant_to_value(cls, subst, self.ctx.root_node)
    elif (isinstance(pyval, pytd.GenericType) and
          pyval.name == "typing.ClassVar"):
      param, = pyval.parameters
      return self.constant_to_value(param, subst, self.ctx.root_node)
    elif isinstance(pyval, pytd.GenericType):
      if isinstance(pyval.base_type, pytd.LateType):
        actual = self._load_late_type(pyval.base_type)
        if not isinstance(actual, pytd.ClassType):
          return self.unsolvable
        base = actual.cls
      else:
        assert isinstance(pyval.base_type, pytd.ClassType), pyval
        base = pyval.base_type.cls
      assert isinstance(base, pytd.Class), base
      base_cls = self.constant_to_value(base, subst, self.ctx.root_node)
      if not isinstance(base_cls, abstract.Class):
        # base_cls can be, e.g., an unsolvable due to an mro error.
        return self.unsolvable
      if isinstance(pyval, pytd.TupleType):
        abstract_class = abstract.TupleClass
        template = list(range(len(pyval.parameters))) + [abstract_utils.T]
        combined_parameter = pytd_utils.JoinTypes(pyval.parameters)
        parameters = pyval.parameters + (combined_parameter,)
      elif isinstance(pyval, pytd.CallableType):
        abstract_class = abstract.CallableClass
        template = list(range(len(pyval.args))) + [abstract_utils.ARGS,
                                                   abstract_utils.RET]
        parameters = pyval.args + (pytd_utils.JoinTypes(pyval.args), pyval.ret)
      else:
        abstract_class = abstract.ParameterizedClass
        if pyval.name == "typing.Generic":
          pyval_template = pyval.parameters
        else:
          pyval_template = base.template
        template = tuple(t.name for t in pyval_template)
        parameters = pyval.parameters
      assert (pyval.name in ("typing.Generic", "typing.Protocol") or
              len(parameters) <= len(template))
      # Delay type parameter loading to handle recursive types.
      # See the ParameterizedClass.formal_type_parameters() property.
      type_parameters = abstract_utils.LazyFormalTypeParameters(
          template, parameters, subst)
      return abstract_class(base_cls, type_parameters, self.ctx)
    elif isinstance(pyval, pytd.Literal):
      value = self.constant_to_value(
          self._get_literal_value(pyval.value), subst, self.ctx.root_node)
      return abstract.LiteralClass(value, self.ctx)
    elif isinstance(pyval, pytd.Annotated):
      typ = self.constant_to_value(pyval.base_type, subst, self.ctx.root_node)
      if pyval.annotations[0] == "'pytype_metadata'":
        try:
          md = metadata.from_string(pyval.annotations[1])
          if md["tag"] == "attr.ib":
            ret = attr_overlay.AttribInstance.from_metadata(
                self.ctx, self.ctx.root_node, typ, md)
            return ret
          elif md["tag"] == "attr.s":
            ret = attr_overlay.Attrs.from_metadata(self.ctx, md)
            return ret
        except (IndexError, ValueError, TypeError, KeyError):
          details = "Wrong format for pytype_metadata."
          self.ctx.errorlog.invalid_annotation(self.ctx.vm.frames,
                                               pyval.annotations[1], details)
          return typ
      else:
        return typ
    elif pyval.__class__ is tuple:  # only match raw tuple, not namedtuple/Node
      return self.tuple_to_value([
          self.constant_to_var(item, subst, self.ctx.root_node)
          for i, item in enumerate(pyval)
      ])
    else:
      raise NotImplementedError("Can't convert constant %s %r" %
                                (type(pyval), pyval))
예제 #16
0
  def _build_namedtuple(self, name, field_names, field_types, node, bases):
    # Build an InterpreterClass representing the namedtuple.
    if field_types:
      # TODO(mdemello): Fix this to support late types.
      field_types_union = abstract.Union(field_types, self.ctx)
    else:
      field_types_union = self.ctx.convert.none_type

    members = {n: t.instantiate(node) for n, t in zip(field_names, field_types)}

    # collections.namedtuple has: __dict__, __slots__ and _fields.
    # typing.NamedTuple adds: _field_types, __annotations__ and _field_defaults.
    # __slots__ and _fields are tuples containing the names of the fields.
    slots = tuple(self.ctx.convert.build_string(node, f) for f in field_names)
    members["__slots__"] = self.ctx.convert.build_tuple(node, slots)
    members["_fields"] = self.ctx.convert.build_tuple(node, slots)
    # __dict__ and _field_defaults are both collections.OrderedDicts that map
    # field names (strings) to objects of the field types.
    ordered_dict_cls = self.ctx.convert.name_to_value(
        "collections.OrderedDict", ast=self.collections_ast)

    # Normally, we would use abstract_utils.K and abstract_utils.V, but
    # collections.pyi doesn't conform to that standard.
    field_dict_cls = abstract.ParameterizedClass(ordered_dict_cls, {
        "K": self.ctx.convert.str_type,
        "V": field_types_union
    }, self.ctx)
    members["__dict__"] = field_dict_cls.instantiate(node)
    members["_field_defaults"] = field_dict_cls.instantiate(node)
    # _field_types and __annotations__ are both collections.OrderedDicts
    # that map field names (strings) to the types of the fields. Note that
    # ctx.make_class will take care of adding the __annotations__ member.
    field_types_cls = abstract.ParameterizedClass(ordered_dict_cls, {
        "K": self.ctx.convert.str_type,
        "V": self.ctx.convert.type_type
    }, self.ctx)
    members["_field_types"] = field_types_cls.instantiate(node)

    # __new__
    # We set the bound on this TypeParameter later. This gives __new__ the
    # signature: def __new__(cls: Type[_Tname], ...) -> _Tname, i.e. the same
    # signature that visitor.CreateTypeParametersForSignatures would create.
    # This allows subclasses of the NamedTuple to get the correct type from
    # their constructors.
    cls_type_param = abstract.TypeParameter(
        visitors.CreateTypeParametersForSignatures.PREFIX + name,
        self.ctx,
        bound=None)
    cls_type = abstract.ParameterizedClass(self.ctx.convert.type_type,
                                           {abstract_utils.T: cls_type_param},
                                           self.ctx)
    params = [Param(n, t) for n, t in zip(field_names, field_types)]
    members["__new__"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="__new__",
        self_param=Param("cls", cls_type),
        params=params,
        return_type=cls_type_param,
    )

    # __init__
    members["__init__"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="__init__",
        varargs=Param("args"),
        kwargs=Param("kwargs"))

    heterogeneous_tuple_type_params = dict(enumerate(field_types))
    heterogeneous_tuple_type_params[abstract_utils.T] = field_types_union
    # Representation of the to-be-created NamedTuple as a typing.Tuple.
    heterogeneous_tuple_type = abstract.TupleClass(
        self.ctx.convert.tuple_type, heterogeneous_tuple_type_params, self.ctx)

    # _make
    # _make is a classmethod, so it needs to be wrapped by
    # special_builtins.ClassMethodInstance.
    # Like __new__, it uses the _Tname TypeVar.
    sized_cls = self.ctx.convert.name_to_value("typing.Sized")
    iterable_type = abstract.ParameterizedClass(
        self.ctx.convert.name_to_value("typing.Iterable"),
        {abstract_utils.T: field_types_union}, self.ctx)
    cls_type = abstract.ParameterizedClass(self.ctx.convert.type_type,
                                           {abstract_utils.T: cls_type_param},
                                           self.ctx)
    len_type = abstract.CallableClass(
        self.ctx.convert.name_to_value("typing.Callable"), {
            0: sized_cls,
            abstract_utils.ARGS: sized_cls,
            abstract_utils.RET: self.ctx.convert.int_type
        }, self.ctx)
    params = [
        Param("iterable", iterable_type),
        Param("new").unsolvable(self.ctx, node),
        Param("len", len_type).unsolvable(self.ctx, node)
    ]
    make = overlay_utils.make_method(
        self.ctx,
        node,
        name="_make",
        params=params,
        self_param=Param("cls", cls_type),
        return_type=cls_type_param)
    make_args = function.Args(posargs=(make,))
    _, members["_make"] = self.ctx.special_builtins["classmethod"].call(
        node, None, make_args)

    # _replace
    # Like __new__, it uses the _Tname TypeVar. We have to annotate the `self`
    # param to make sure the TypeVar is substituted correctly.
    members["_replace"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="_replace",
        self_param=Param("self", cls_type_param),
        return_type=cls_type_param,
        kwargs=Param("kwds", field_types_union))

    # __getnewargs__
    members["__getnewargs__"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="__getnewargs__",
        return_type=heterogeneous_tuple_type)

    # __getstate__
    members["__getstate__"] = overlay_utils.make_method(
        self.ctx, node, name="__getstate__")

    # _asdict
    members["_asdict"] = overlay_utils.make_method(
        self.ctx, node, name="_asdict", return_type=field_dict_cls)

    # Finally, make the class.
    cls_dict = abstract.Dict(self.ctx)
    cls_dict.update(node, members)

    if self.ctx.options.strict_namedtuple_checks:
      # Enforces type checking like Tuple[...]
      superclass_of_new_type = heterogeneous_tuple_type.to_variable(node)
    else:
      superclass_of_new_type = self.ctx.convert.tuple_type.to_variable(node)
    if bases:
      final_bases = []
      for base in bases:
        if any(b.full_name == "typing.NamedTuple" for b in base.data):
          final_bases.append(superclass_of_new_type)
        else:
          final_bases.append(base)
    else:
      final_bases = [superclass_of_new_type]
      # This NamedTuple is being created via a function call. We manually
      # construct an annotated_locals entry for it so that __annotations__ is
      # initialized properly for the generated class.
      self.ctx.vm.annotated_locals[name] = {
          field: abstract_utils.Local(node, None, typ, None, self.ctx)
          for field, typ in zip(field_names, field_types)
      }

    node, cls_var = self.ctx.make_class(
        node=node,
        name_var=self.ctx.convert.build_string(node, name),
        bases=final_bases,
        class_dict_var=cls_dict.to_variable(node),
        cls_var=None)
    cls = cls_var.data[0]

    # Now that the class has been made, we can complete the TypeParameter used
    # by __new__, _make and _replace.
    cls_type_param.bound = cls

    return node, cls_var