diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 136f304..3e1b7a4 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -2735,7 +2735,9 @@ warn_if_shadowing (tree new_decl)
   struct c_binding *b;
 
   /* Shadow warnings wanted?  */
-  if (!warn_shadow
+  if (!(warn_shadow
+        || warn_shadow_local
+        || warn_shadow_compatible_local)
       /* No shadow warnings for internally generated vars.  */
       || DECL_IS_BUILTIN (new_decl)
       /* No shadow warnings for vars made for inlining.  */
@@ -2759,9 +2761,23 @@ warn_if_shadowing (tree new_decl)
 	    break;
 	  }
 	else if (TREE_CODE (old_decl) == PARM_DECL)
-	  warned = warning (OPT_Wshadow,
-			    "declaration of %q+D shadows a parameter",
-			    new_decl);
+	  {
+	    enum opt_code warning_code;
+
+	    /* If '-Wshadow=compatible-local' is specified without other
+	       -Wshadow= flags, we will warn only when the types of the
+	       shadowing variable (i.e. new_decl) and the shadowed variable
+	       (old_decl) are compatible.  */
+	    if (warn_shadow)
+	      warning_code = OPT_Wshadow;
+	    else if (comptypes (TREE_TYPE (old_decl), TREE_TYPE (new_decl)))
+	      warning_code = OPT_Wshadow_compatible_local;
+	    else
+	      warning_code = OPT_Wshadow_local;
+	    warned = warning_at (DECL_SOURCE_LOCATION (new_decl), warning_code,
+				 "declaration of %qD shadows a parameter",
+				 new_decl);
+	  }
 	else if (DECL_FILE_SCOPE_P (old_decl))
 	  {
 	    /* Do not warn if a variable shadows a function, unless
@@ -2784,8 +2800,23 @@ warn_if_shadowing (tree new_decl)
 	    break;
 	  }
 	else
-	  warned = warning (OPT_Wshadow, "declaration of %q+D shadows a "
-			    "previous local", new_decl);
+	  {
+	    enum opt_code warning_code;
+
+	    /* If '-Wshadow=compatible-local' is specified without other
+	       -Wshadow= flags, we will warn only when the types of the
+	       shadowing variable (i.e. new_decl) and the shadowed variable
+	       (old_decl) are compatible.  */
+	    if (warn_shadow)
+	      warning_code = OPT_Wshadow;
+	    else if (comptypes (TREE_TYPE (old_decl), TREE_TYPE (new_decl)))
+	      warning_code = OPT_Wshadow_compatible_local;
+	    else
+	      warning_code = OPT_Wshadow_local;
+	    warned = warning_at (DECL_SOURCE_LOCATION (new_decl), warning_code,
+				 "declaration of %qD shadows a previous local",
+				 new_decl);
+	  }
 
 	if (warned)
 	  inform (DECL_SOURCE_LOCATION (old_decl),
diff --git a/gcc/common.opt b/gcc/common.opt
index 20dbfc1..2c2f7ad 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -668,7 +668,25 @@ Warn about returning a pointer/reference to a local or temporary variable.
 
 Wshadow
 Common Var(warn_shadow) Warning
-Warn when one local variable shadows another.
+Warn when one variable shadows another.  Same as -Wshadow=global.
+
+Wshadow=global
+Common Warning Alias(Wshadow)
+Warn when one variable shadows another (globally).
+
+Wshadow=local
+Common Var(warn_shadow_local) Warning EnabledBy(Wshadow)
+Warn when one local variable shadows another local variable or parameter.
+
+Wshadow-local
+Common Warning Undocumented Alias(Wshadow=local)
+
+Wshadow=compatible-local
+Common Var(warn_shadow_compatible_local) Warning EnabledBy(Wshadow=local)
+Warn when one local variable shadows another local variable or parameter of compatible type.
+
+Wshadow-compatible-local
+Common Warning Undocumented Alias(Wshadow=compatible-local)
 
 Wstack-protector
 Common Var(warn_stack_protect) Warning
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 9e84a1b..023ed87 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -1195,19 +1195,41 @@ pushdecl_maybe_friend_1 (tree x, bool is_friend)
 		  nowarn = true;
 		}
 
-	      if (warn_shadow && !nowarn)
+	      if ((warn_shadow
+		   || warn_shadow_local
+		   || warn_shadow_compatible_local)
+		  && !nowarn)
 		{
 		  bool warned;
+		  enum opt_code warning_code;
+		  /* If '-Wshadow=compatible-local' is specified without other
+		     -Wshadow= flags, we will warn only when the type of the
+		     shadowing variable (i.e. x) can be converted to that of
+		     the shadowed parameter (oldlocal). The reason why we only
+		     check if x's type can be converted to oldlocal's type
+		     (but not the other way around) is because when users
+		     accidentally shadow a parameter, more than often they
+		     would use the variable thinking (mistakenly) it's still
+		     the parameter. It would be rare that users would use the
+		     variable in the place that expects the parameter but
+		     thinking it's a new decl.  */
+		  if (warn_shadow)
+		    warning_code = OPT_Wshadow;
+		  else if (can_convert (TREE_TYPE (oldlocal), TREE_TYPE (x),
+					tf_none))
+		    warning_code = OPT_Wshadow_compatible_local;
+		  else
+		    warning_code = OPT_Wshadow_local;
 
 		  if (TREE_CODE (oldlocal) == PARM_DECL)
-		    warned = warning_at (input_location, OPT_Wshadow,
+		    warned = warning_at (input_location, warning_code,
 				"declaration of %q#D shadows a parameter", x);
 		  else if (is_capture_proxy (oldlocal))
-		    warned = warning_at (input_location, OPT_Wshadow,
+		    warned = warning_at (input_location, warning_code,
 				"declaration of %qD shadows a lambda capture",
 				x);
 		  else
-		    warned = warning_at (input_location, OPT_Wshadow,
+		    warned = warning_at (input_location, warning_code,
 				"declaration of %qD shadows a previous local",
 				x);
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 242eed7..fd783bf 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -293,6 +293,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wpointer-arith  -Wno-pointer-to-int-cast @gol
 -Wno-pragmas -Wredundant-decls  -Wno-return-local-addr @gol
 -Wreturn-type  -Wsequence-point  -Wshadow  -Wno-shadow-ivar @gol
+-Wshadow=global, -Wshadow=local, -Wshadow=compatible-local @gol
 -Wshift-overflow -Wshift-overflow=@var{n} @gol
 -Wshift-count-negative -Wshift-count-overflow -Wshift-negative-value @gol
 -Wsign-compare  -Wsign-conversion -Wfloat-conversion @gol
@@ -5321,6 +5322,7 @@ variable, parameter, type, class member (in C++), or instance variable
 (in Objective-C) or whenever a built-in function is shadowed. Note
 that in C++, the compiler warns if a local variable shadows an
 explicit typedef, but not if it shadows a struct/class/enum.
+Same as @option{-Wshadow=global}.
 
 @item -Wno-shadow-ivar @r{(Objective-C only)}
 @opindex Wno-shadow-ivar
@@ -5328,6 +5330,48 @@ explicit typedef, but not if it shadows a struct/class/enum.
 Do not warn whenever a local variable shadows an instance variable in an
 Objective-C method.
 
+@item -Wshadow=global
+@opindex Wshadow=local
+The default for @option{-Wshadow}. Warns for any (global) shadowing.
+
+@item -Wshadow=local
+@opindex Wshadow=local
+Warn when a local variable shadows another local variable or parameter.
+This warning is enabled by @option{-Wshadow=global}.
+
+@item -Wshadow=compatible-local
+@opindex Wshadow=compatible-local
+Warn when a local variable shadows another local variable or parameter
+whose type is compatible with that of the shadowing variable. In C++,
+type compatibility here means the type of the shadowing variable can be
+converted to that of the shadowed variable. The creation of this flag
+(in addition to @option{-Wshadow=local}) is based on the idea that when
+a local variable shadows another one of incompatible type, it is most
+likely intentional, not a bug or typo, as shown in the following example:
+
+@smallexample
+@group
+for (SomeIterator i = SomeObj.begin(); i != SomeObj.end(); ++i)
+@{
+  for (int i = 0; i < N; ++i)
+  @{
+    ...
+  @}
+  ...
+@}
+@end group
+@end smallexample
+
+Since the two variable @code{i} in the example above have incompatible types,
+enabling only @option{-Wshadow=compatible-local} will not emit a warning.
+Because their types are incompatible, if a programmer accidentally uses one
+in place of the other, type checking will catch that and emit an error or
+warning. So not warning (about shadowing) in this case will not lead to
+undetected bugs. Use of this flag instead of @option{-Wshadow=local} can
+possibly reduce the number of warnings triggered by intentional shadowing.
+
+This warning is enabled by @option{-Wshadow=local}.
+
 @item -Wlarger-than=@var{len}
 @opindex Wlarger-than=@var{len}
 @opindex Wlarger-than-@var{len}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 7ebc616..1847d22 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,14 @@
+2016-10-30  Le-Chun Wu  <lcwu@google.com>
+	    Mark Wielaard  <mjw@redhat.com>
+
+	* gcc.dg/Wshadow-compatible-local-1.c: New test.
+	* gcc.dg/Wshadow-local-1.c: Likewise.
+	* gcc.dg/Wshadow-local-2.c: Likewise.
+	* g++.dg/warn/Wshadow-compatible-local-1.C: Likewise.
+	* g++.dg/warn/Wshadow-local-1.C: Likewise.
+	* g++.dg/warn/Wshadow-local-2.C: Likewise.
+
+
 2016-10-29  Jakub Jelinek  <jakub@redhat.com>
 
 	PR rtl-optimization/77919
diff --git a/gcc/testsuite/g++.dg/warn/Wshadow-compatible-local-1.C b/gcc/testsuite/g++.dg/warn/Wshadow-compatible-local-1.C
new file mode 100644
index 0000000..aac6861
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wshadow-compatible-local-1.C
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options -Wshadow=compatible-local } */
+
+class Bar {
+};
+
+class ChildBar : public Bar {
+};
+
+Bar bar;
+
+class Foo {
+ private:
+  int val;
+
+ public:
+  int func1(int x) {
+    int val;
+    val = x;
+    return val;
+  }
+
+  int func2(int i) { // { dg-message "note: shadowed declaration is here" }
+    int a = 3;       // { dg-message "note: shadowed declaration is here" }
+
+    for (int i = 0; i < 5; ++i) {   // { dg-warning "shadows a parameter" }
+      for (int i = 0; i < 3; ++i) { // { dg-warning "shadows a previous local" }
+        int a = i;   // { dg-warning "shadows a previous local" }
+        func1(a);
+      }
+    }
+
+    return a;
+  }
+
+  int func3() {
+    int bar;
+    float func1 = 0.3;
+    int f = 5;       // { dg-message "note: shadowed declaration is here" }
+
+    if (func1 > 1) {
+      float f = 2.0; // { dg-warning "shadows a previous local" }
+      bar = f;
+    }
+    else
+      bar = 1;
+    return bar;
+  }
+
+  void func4() {
+    Bar *bar;        // { dg-bogus "shadowed declaration" }
+    ChildBar *cbp;   // { dg-bogus "shadowed declaration" }
+    Bar *bp;         // { dg-message "note: shadowed declaration is here" }
+    if (val) {
+      int bar;       // { dg-bogus "shadows a previous local" }
+      Bar *cbp;      // { dg-bogus "shadows a previous local" }
+      ChildBar *bp;  // { dg-warning "shadows a previous local" }
+      func1(bar);
+    }
+  }
+};
+
+// { dg-message "note: shadowed declaration" "" { target *-*-* } 26 }
diff --git a/gcc/testsuite/g++.dg/warn/Wshadow-local-1.C b/gcc/testsuite/g++.dg/warn/Wshadow-local-1.C
new file mode 100644
index 0000000..dba6db5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wshadow-local-1.C
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options -Wshadow=local } */
+
+struct status
+{
+  int member;
+  void foo2 ();
+
+  inline static int foo3 (int member)
+  {
+    return member;
+  }
+};
+
+int decl1;                      // { dg-bogus "shadowed declaration" }
+int decl2;                      // { dg-bogus "shadowed declaration" }
+void foo (struct status &status,
+	  double decl1)		// { dg-bogus "shadows a global" }
+{
+}
+
+void foo1 (int d)
+{
+  double d;			// { dg-error "shadows a parameter" }
+}
+
+void status::foo2 ()
+{
+  int member;			// { dg-bogus "shadows a member" }
+  int decl2;			// { dg-bogus "shadows a global" }
+  int local;			// { dg-message "note: shadowed declaration is here" }
+  {
+    int local;			// { dg-warning "shadows a previous local" }
+  }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wshadow-local-2.C b/gcc/testsuite/g++.dg/warn/Wshadow-local-2.C
new file mode 100644
index 0000000..fe42c89
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wshadow-local-2.C
@@ -0,0 +1,63 @@
+/* { dg-do compile } */
+/* { dg-options -Wshadow=local } */
+
+class Bar {
+};
+
+class ChildBar : public Bar {
+};
+
+Bar bar;             // { dg-bogus "shadowed declaration" }
+
+class Foo {
+ private:
+  int val;
+
+ public:
+  int func1(int x) {
+    int val;         // { dg-bogus "shadows a member" }
+    val = x;
+    return val;
+  }
+
+  int func2(int i) { // { dg-message "shadowed declaration is here" }
+    int a = 3;       // { dg-message "shadowed declaration is here" }
+
+    for (int i = 0; i < 5; ++i) {   // { dg-warning "shadows a parameter" }
+      for (int i = 0; i < 3; ++i) { // { dg-warning "shadows a previous local" }
+        int a = i;   // { dg-warning "shadows a previous local" }
+        func1(a);
+      }
+    }
+
+    return a;
+  }
+
+  int func3() {
+    int bar;         // { dg-bogus "shadows a global" }
+    float func1 = 0.3; // { dg-bogus "shadows a member" }
+    int f = 5;       // { dg-message "shadowed declaration is here" }
+
+    if (func1 > 1) {
+      float f = 2.0; // { dg-warning "shadows a previous local" }
+      bar = f;
+    }
+    else
+      bar = 1;
+    return bar;
+  }
+
+  void func4() {
+    Bar *bar;        // { dg-message "shadowed declaration is here" }
+    ChildBar *cbp;   // { dg-message "shadowed declaration is here" }
+    Bar *bp;         // { dg-message "shadowed declaration is here" }
+    if (val) {
+      int bar;       // { dg-warning "shadows a previous local" }
+      Bar *cbp;      // { dg-warning "shadows a previous local" }
+      ChildBar *bp;  // { dg-warning "shadows a previous local" }
+      func1(bar);
+    }
+  }
+};
+
+// { dg-message "shadowed declaration is here" "" { target *-*-* } 26 }
diff --git a/gcc/testsuite/gcc.dg/Wshadow-compatible-local-1.c b/gcc/testsuite/gcc.dg/Wshadow-compatible-local-1.c
new file mode 100644
index 0000000..ed7b2e4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wshadow-compatible-local-1.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-Wshadow=compatible-local" } */
+
+struct Bar {
+};
+
+struct Bar bar;       /* { dg-bogus "shadowed declaration" } */
+
+int val;              /* { dg-bogus "shadowed declaration" } */
+
+int func1(int x) {    /* { dg-bogus "shadowed declaration" } */
+  int val;            /* { dg-bogus "shadows a global" } */
+  val = x;
+  return val;
+}
+
+int func2(int i) {
+  int a = 3;          /* { dg-message "shadowed declaration" } */
+  int j;              /* { dg-message "shadowed declaration" } */
+
+  for (j = 0; j < i; ++j) {
+    int a = j;        /* { dg-warning "shadows a previous local" } */
+    int j = a + 1;    /* { dg-warning "shadows a previous local" } */
+    func1(j);
+  }
+
+  return a;
+}
+
+void func4() {
+  struct Bar bar;     /* { dg-bogus "shadowed declaration" } */
+  if (val) {
+    int bar;          /* { dg-bogus "shadows a previous local" } */
+    func1(bar);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wshadow-local-1.c b/gcc/testsuite/gcc.dg/Wshadow-local-1.c
new file mode 100644
index 0000000..161f994
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wshadow-local-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-Wshadow=local" } */
+
+int decl1;			/* should not warn */
+void foo (double decl1)		/* should not warn */
+{				
+}
+
+void foo2 (int d)		/* { dg-message "shadowed declaration" } */
+{
+  {
+    double d;			/* { dg-warning "shadows a parameter" } */
+  }
+}
+
+void foo3 ()
+{
+  int local;			/* { dg-message "shadowed declaration" } */
+  {
+    int local;			/* { dg-warning "shadows a previous local" } */
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wshadow-local-2.c b/gcc/testsuite/gcc.dg/Wshadow-local-2.c
new file mode 100644
index 0000000..8f6b132
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wshadow-local-2.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-Wshadow=local" } */
+
+struct Bar {
+};
+
+struct Bar bar;       /* { dg-bogus "shadowed declaration" } */
+
+int val;              /* { dg-bogus "shadowed declaration" } */
+
+int func1(int x) {    /* { dg-bogus "shadowed declaration" } */
+  int val;            /* { dg-bogus "shadows a global" } */
+  val = x;
+  return val;
+}
+
+int func2(int i) {
+  int a = 3;          /* { dg-message "shadowed declaration" } */
+  int j;              /* { dg-message "shadowed declaration" } */
+
+  for (j = 0; j < i; ++j) {
+    int a = j;        /* { dg-warning "shadows a previous local" } */
+    int j = a + 1;    /* { dg-warning "shadows a previous local" } */
+    func1(j);
+  }
+
+  return a;
+}
+
+int func3() {
+  int bar;            /* { dg-bogus "shadows a global" } */
+  float func1 = 0.3;  /* { dg-bogus "shadows a global" } */
+
+  if (func1 > 1)
+    bar = 2;
+  else
+    bar = 1;
+  return bar;
+}
+
+void func4() {
+  struct Bar bar;     /* { dg-message "shadowed declaration" } */
+  if (val) {
+    int bar;          /* { dg-warning "shadows a previous local" } */
+    func1(bar);
+  }
+}
+
+/* { dg-bogus "shadows a global" ""  { target *-*-* } 42 } */
diff --git a/gcc/testsuite/gcc.dg/Wshadow-local-3.c b/gcc/testsuite/gcc.dg/Wshadow-local-3.c
new file mode 100644
index 0000000..429df37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wshadow-local-3.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-Wno-shadow" } */
+
+void func() {
+  int i;
+    {
+      int i; /* should not warn */
+    }
+}
