bpo-40147: Move the check for duplicate keywords to the compiler (GH-19289)

diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py
index e6fe20a..6edd899 100644
--- a/Lib/test/test_metaclass.py
+++ b/Lib/test/test_metaclass.py
@@ -128,7 +128,7 @@
     ...
     Traceback (most recent call last):
     [...]
-    SyntaxError: keyword argument repeated
+    SyntaxError: keyword argument repeated: metaclass
     >>>
 
 Another way.
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 128c4da..a7e7e2c 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -588,7 +588,7 @@
 >>> f(a=23, a=234)
 Traceback (most recent call last):
    ...
-SyntaxError: keyword argument repeated
+SyntaxError: keyword argument repeated: a
 
 >>> {1, 2, 3} = 42
 Traceback (most recent call last):
diff --git a/Python/ast.c b/Python/ast.c
index 550ee03..6ba62fb 100644
--- a/Python/ast.c
+++ b/Python/ast.c
@@ -3048,8 +3048,7 @@
             else {
                 /* a keyword argument */
                 keyword_ty kw;
-                identifier key, tmp;
-                int k;
+                identifier key;
 
                 // To remain LL(1), the grammar accepts any test (basically, any
                 // expression) in the keyword slot of a call site.  So, we need
@@ -3093,14 +3092,6 @@
                 if (forbidden_name(c, key, chch, 1)) {
                     return NULL;
                 }
-                for (k = 0; k < nkeywords; k++) {
-                    tmp = ((keyword_ty)asdl_seq_GET(keywords, k))->arg;
-                    if (tmp && !PyUnicode_Compare(tmp, key)) {
-                        ast_error(c, chch,
-                                  "keyword argument repeated");
-                        return NULL;
-                    }
-                }
                 e = ast_for_expr(c, CHILD(ch, 2));
                 if (!e)
                     return NULL;
diff --git a/Python/compile.c b/Python/compile.c
index 01700e0..b1c1982 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -4050,6 +4050,31 @@
 }
 
 static int
+validate_keywords(struct compiler *c, asdl_seq* keywords) {
+    int nkeywords = asdl_seq_LEN(keywords);
+    for (int i = 0; i < nkeywords; i++) {
+        keyword_ty key = ((keyword_ty)asdl_seq_GET(keywords, i));
+        if (key->arg == NULL) {
+            continue;
+        }
+        for (int j = i+1; j < nkeywords; j++) {
+            keyword_ty other = ((keyword_ty)asdl_seq_GET(keywords, j));
+            if (other->arg && !PyUnicode_Compare(key->arg, other->arg)) {
+                PyObject *msg = PyUnicode_FromFormat("keyword argument repeated: %U", key->arg);
+                if (msg == NULL) {
+                    return -1;
+                }
+                c->u->u_col_offset = other->col_offset;
+                compiler_error(c, PyUnicode_AsUTF8(msg));
+                Py_DECREF(msg);
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
+
+static int
 compiler_call(struct compiler *c, expr_ty e)
 {
     int ret = maybe_optimize_method_call(c, e);
@@ -4165,6 +4190,10 @@
 {
     Py_ssize_t i, nseen, nelts, nkwelts;
 
+    if (validate_keywords(c, keywords) == -1) {
+        return 0;
+    }
+
     nelts = asdl_seq_LEN(args);
     nkwelts = asdl_seq_LEN(keywords);