diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 237207a5ef49066597d63c9eaded4119b09c420b..c9984460b3dc73afb961a51492b31e780d5b4827 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,9 @@
+2004-01-07  Joseph S. Myers  <jsm@polyomino.org.uk>
+
+	PR c/12165
+	* c-decl.c (grokdeclarator): Take type qualifiers of typedefed
+	array type from the array element type.
+
 2004-01-07  Alan Modra  <amodra@bigpond.net.au>
 
 	* config/rs6000/rs6000.c (rs6000_dbx_register_number): New function.
diff --git a/gcc/c-decl.c b/gcc/c-decl.c
index ee2db9e1fc42ac94378bedd667725d5b08dea629..b41ed8671bcd4c3097d75678efa194f1a03ecccf 100644
--- a/gcc/c-decl.c
+++ b/gcc/c-decl.c
@@ -1,6 +1,6 @@
 /* Process declarations and variables for C compiler.
    Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003 Free Software Foundation, Inc.
+   2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -3349,6 +3349,7 @@ grokdeclarator (tree declarator, tree declspecs,
   int array_parm_static = 0;
   tree returned_attrs = NULL_TREE;
   bool bitfield = width != NULL;
+  tree element_type;
 
   if (decl_context == FUNCDEF)
     funcdef_flag = 1, decl_context = NORMAL;
@@ -3694,10 +3695,19 @@ grokdeclarator (tree declarator, tree declspecs,
      two ways a declaration can become qualified.  One is something
      like `const int i' where the `const' is explicit.  Another is
      something like `typedef const int CI; CI i' where the type of the
-     declaration contains the `const'.  */
-  constp = !! (specbits & 1 << (int) RID_CONST) + TYPE_READONLY (type);
-  restrictp = !! (specbits & 1 << (int) RID_RESTRICT) + TYPE_RESTRICT (type);
-  volatilep = !! (specbits & 1 << (int) RID_VOLATILE) + TYPE_VOLATILE (type);
+     declaration contains the `const'.  A third possibility is that
+     there is a type qualifier on the element type of a typedefed
+     array type, in which case we should extract that qualifier so
+     that c_apply_type_quals_to_decls receives the full list of
+     qualifiers to work with (C90 is not entirely clear about whether
+     duplicate qualifiers should be diagnosed in this case, but it
+     seems most appropriate to do so).  */
+  element_type = strip_array_types (type);
+  constp = !! (specbits & 1 << (int) RID_CONST) + TYPE_READONLY (element_type);
+  restrictp
+    = !! (specbits & 1 << (int) RID_RESTRICT) + TYPE_RESTRICT (element_type);
+  volatilep
+    = !! (specbits & 1 << (int) RID_VOLATILE) + TYPE_VOLATILE (element_type);
   inlinep = !! (specbits & (1 << (int) RID_INLINE));
   if (constp > 1 && ! flag_isoc99)
     pedwarn ("duplicate `const'");
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index f57a826865238fdc2fa083035a6c23fce5691972..020ffd89b924db49f735398efc84bb066f2dfa31 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2004-01-07  Joseph S. Myers  <jsm@polyomino.org.uk>
+
+	PR c/12165
+	* gcc.dg/array-quals-1.c, gcc.dg/c90-idem-qual-3.c,
+	gcc.dg/c99-idem-qual-3.c: New tests.
+
 2004-01-07  Alan Modra  <amodra@bigpond.net.au>
 
 	* gcc.dg/winline-7.c: Don't cast void * to int.
diff --git a/gcc/testsuite/gcc.dg/array-quals-1.c b/gcc/testsuite/gcc.dg/array-quals-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d2b72c6b980b7e9b3507e223c756818b5c96f94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/array-quals-1.c
@@ -0,0 +1,29 @@
+/* Test for various combinations of const, arrays and typedefs:
+   should never actually get const on the final array type, but
+   all should end up in a read-only section.  PR c/12165.  */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { dg-do compile } */
+/* { dg-final { scan-assembler-not "\\.data" } } */
+static const int a[2] = { 1, 2 };
+const int a1[2] = { 1, 2 };
+typedef const int ci;
+static ci b[2] = { 3, 4 };
+ci b1[2] = { 3, 4 };
+typedef int ia[2];
+static const ia c = { 5, 6 };
+const ia c1 = { 5, 6 };
+typedef const int cia[2];
+static cia d = { 7, 8 };
+cia d1 = { 7, 8 };
+static cia e[2] = { { 1, 2 }, { 3, 4 } };
+cia e1[2] = { { 1, 2 }, { 3, 4 } };
+void *const p = &a;
+void *const q = &b;
+void *const r = &c;
+void *const s = &d;
+void *const t = &e;
+void *const p1 = &a1;
+void *const q1 = &b1;
+void *const r1 = &c1;
+void *const s1 = &d1;
+void *const t1 = &e1;
diff --git a/gcc/testsuite/gcc.dg/c90-idem-qual-3.c b/gcc/testsuite/gcc.dg/c90-idem-qual-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..9976a0888fb51f6f36c971fed806e8ea6a04cb2c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c90-idem-qual-3.c
@@ -0,0 +1,11 @@
+/* Test for idempotent type qualifiers: in C99 only.  Test duplicate
+   type qualifiers with array element types.  */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic-errors" } */
+
+typedef const int cia[2];
+const cia a; /* { dg-bogus "warning" "warning in place of error" } */
+/* { dg-error "duplicate" "duplicate type qualifier error" { target *-*-* } 8 } */
+const cia b[2]; /* { dg-bogus "warning" "warning in place of error" } */
+/* { dg-error "duplicate" "duplicate type qualifier error" { target *-*-* } 10 } */
diff --git a/gcc/testsuite/gcc.dg/c99-idem-qual-3.c b/gcc/testsuite/gcc.dg/c99-idem-qual-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..0f34f832188661c5472a61c59c3f2035b562d42d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c99-idem-qual-3.c
@@ -0,0 +1,9 @@
+/* Test for idempotent type qualifiers: in C99 only.  Test duplicate
+   type qualifiers with array element types.  */
+/* Origin: Joseph Myers <jsm@polyomino.org.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
+
+typedef const int cia[2];
+const cia a; /* { dg-bogus "duplicate" "duplicate type qualifier warning" } */
+const cia b[2]; /* { dg-bogus "duplicate" "duplicate type qualifier warning" } */