Issue #19611: handle implicit parameters in inspect.signature

inspect.signature now reports the implicit ``.0`` parameters generated by
the compiler for comprehension and generator expression scopes as if they
were positional-only parameters called ``implicit0``.

Patch by Jelle Zijlstra.
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 3994850..3b36379 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -625,6 +625,16 @@
       The name of the parameter as a string.  The name must be a valid
       Python identifier.
 
+      .. impl-detail::
+
+         CPython generates implicit parameter names of the form ``.0`` on the
+         code objects used to implement comprehensions and generator
+         expressions.
+
+         .. versionchanged:: 3.6
+            These parameter names are exposed by this module as names like
+            ``implicit0``.
+
    .. attribute:: Parameter.default
 
       The default value for the parameter.  If the parameter has no default
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 582bb0e..45d2110 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -2396,6 +2396,20 @@
         if not isinstance(name, str):
             raise TypeError("name must be a str, not a {!r}".format(name))
 
+        if name[0] == '.' and name[1:].isdigit():
+            # These are implicit arguments generated by comprehensions. In
+            # order to provide a friendlier interface to users, we recast
+            # their name as "implicitN" and treat them as positional-only.
+            # See issue 19611.
+            if kind != _POSITIONAL_OR_KEYWORD:
+                raise ValueError(
+                    'implicit arguments must be passed in as {}'.format(
+                        _POSITIONAL_OR_KEYWORD
+                    )
+                )
+            self._kind = _POSITIONAL_ONLY
+            name = 'implicit{}'.format(name[1:])
+
         if not name.isidentifier():
             raise ValueError('{!r} is not a valid parameter name'.format(name))
 
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 76cebcc..47244ae 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -2903,6 +2903,10 @@
                                     'is not a valid parameter name'):
             inspect.Parameter('$', kind=inspect.Parameter.VAR_KEYWORD)
 
+        with self.assertRaisesRegex(ValueError,
+                                    'is not a valid parameter name'):
+            inspect.Parameter('.a', kind=inspect.Parameter.VAR_KEYWORD)
+
         with self.assertRaisesRegex(ValueError, 'cannot have default values'):
             inspect.Parameter('a', default=42,
                               kind=inspect.Parameter.VAR_KEYWORD)
@@ -2986,6 +2990,17 @@
         with self.assertRaisesRegex(TypeError, 'name must be a str'):
             inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY)
 
+    @cpython_only
+    def test_signature_parameter_implicit(self):
+        with self.assertRaisesRegex(ValueError,
+                                    'implicit arguments must be passed in as'):
+            inspect.Parameter('.0', kind=inspect.Parameter.POSITIONAL_ONLY)
+
+        param = inspect.Parameter(
+            '.0', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
+        self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_ONLY)
+        self.assertEqual(param.name, 'implicit0')
+
     def test_signature_parameter_immutability(self):
         p = inspect.Parameter('spam', kind=inspect.Parameter.KEYWORD_ONLY)
 
@@ -3234,6 +3249,17 @@
         ba = sig.bind(args=1)
         self.assertEqual(ba.arguments, {'kwargs': {'args': 1}})
 
+    @cpython_only
+    def test_signature_bind_implicit_arg(self):
+        # Issue #19611: getcallargs should work with set comprehensions
+        def make_set():
+            return {z * z for z in range(5)}
+        setcomp_code = make_set.__code__.co_consts[1]
+        setcomp_func = types.FunctionType(setcomp_code, {})
+
+        iterator = iter(range(5))
+        self.assertEqual(self.call(setcomp_func, iterator), {0, 1, 4, 9, 16})
+
 
 class TestBoundArguments(unittest.TestCase):
     def test_signature_bound_arguments_unhashable(self):
diff --git a/Misc/ACKS b/Misc/ACKS
index ee67e27..7f289fb 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1665,6 +1665,7 @@
 Cheng Zhang
 Kai Zhu
 Tarek Ziadé
+Jelle Zijlstra
 Gennadiy Zlobin
 Doug Zongker
 Peter Åstrand
diff --git a/Misc/NEWS b/Misc/NEWS
index 5a1d4d5..0dc317e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,11 @@
 Library
 -------
 
+- Issue #19611: :mod:`inspect` now reports the implicit ``.0`` parameters
+  generated by the compiler for comprehension and generator expression scopes
+  as if they were positional-only parameters called ``implicit0``.
+  Patch by Jelle Zijlstra.
+
 - Issue #26809: Add ``__all__`` to :mod:`string`.  Patch by Emanuel Barry.
 
 - Issue #26373: subprocess.Popen.communicate now correctly ignores