diff --git a/gdb/eval.c b/gdb/eval.c
index 2a39774..80dfb2e 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -2727,18 +2727,7 @@ evaluate_subexp_standard (struct type *expect_type,
       if (noside == EVAL_SKIP)
         goto nosideret;
       else if (noside == EVAL_AVOID_SIDE_EFFECTS)
-	{
-	  struct type *type = exp->elts[pc + 1].type;
-
-	  /* If this is a typedef, then find its immediate target.  We
-	     use check_typedef to resolve stubs, but we ignore its
-	     result because we do not want to dig past all
-	     typedefs.  */
-	  check_typedef (type);
-	  if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
-	    type = TYPE_TARGET_TYPE (type);
-	  return allocate_value (type);
-	}
+	return allocate_value (exp->elts[pc + 1].type);
       else
         error (_("Attempt to use a type name as an expression"));
 
diff --git a/gdb/testsuite/gdb.base/dfp-test.c b/gdb/testsuite/gdb.base/dfp-test.c
index 3af2b4d..a184acb 100644
--- a/gdb/testsuite/gdb.base/dfp-test.c
+++ b/gdb/testsuite/gdb.base/dfp-test.c
@@ -91,6 +91,23 @@ volatile _Decimal32 d32;
 volatile _Decimal64 d64;
 volatile _Decimal128 d128;
 
+/* Typedefs and typedefs of typedefs, for ptype/whatis testing.  */
+typedef _Decimal32 d32_t;
+typedef _Decimal64 d64_t;
+typedef _Decimal128 d128_t;
+
+typedef d32_t d32_t2;
+typedef d64_t d64_t2;
+typedef d128_t d128_t2;
+
+d32_t v_d32_t;
+d64_t v_d64_t;
+d128_t v_d128_t;
+
+d32_t2 v_d32_t2;
+d64_t2 v_d64_t2;
+d128_t2 v_d128_t2;
+
 struct decstruct
 {
   int int4;
diff --git a/gdb/testsuite/gdb.base/dfp-test.exp b/gdb/testsuite/gdb.base/dfp-test.exp
index 5f7b13d..c3a51a4 100644
--- a/gdb/testsuite/gdb.base/dfp-test.exp
+++ b/gdb/testsuite/gdb.base/dfp-test.exp
@@ -274,6 +274,10 @@ gdb_test "ptype d64 + ds.dec32" " = volatile _Decimal64"
 gdb_test "ptype d128 + ds.dec32" " = volatile _Decimal128"
 gdb_test "ptype d128 + ds.dec64" " = volatile _Decimal128"
 
+gdb_test "whatis d64 + ds.dec32" " = volatile _Decimal64"
+gdb_test "whatis d128 + ds.dec32" " = volatile _Decimal128"
+gdb_test "whatis d128 + ds.dec64" " = volatile _Decimal128"
+
 # Mixture of Decimal and integral operands
 gdb_test "p d32 + 1" " = 1.1"
 gdb_test "p 2 + d64" " = 2.1"
@@ -331,3 +335,58 @@ gdb_test "print ds.dec128 = -ds.double8" " = 0.(0999.*|1000.*)"
 gdb_test "print ds.dec128 = ds.dec32" " = -0.1"
 gdb_test "print ds.dec32 = ds.int4" " = 1"
 gdb_test "print ds.int4 = 7.3dl" " = 7"
+
+# Test "whatis"/"ptype" of expressions involving casts to/from dfp
+# typedefs.
+
+# This list is composed by sub-lists, and their elements are (in
+# order):
+#
+# - Type to cast to.  This is also what "whatis" should print.
+# - What "ptype" should print.
+
+# Columns in the sublists represent:
+     # to/whatis   # ptype
+foreach elem {
+    {"_Decimal32"  "_Decimal32"}
+    {"_Decimal64"  "_Decimal64"}
+    {"_Decimal128" "_Decimal128"}
+    {"d32_t"       "_Decimal32"}
+    {"d64_t"       "_Decimal64"}
+    {"d128_t"      "_Decimal128"}
+    {"d32_t2"      "_Decimal32"}
+    {"d64_t2"      "_Decimal64"}
+    {"d128_t2"     "_Decimal128"}
+} {
+    set type [lindex $elem 0]
+    set ptype [lindex $elem 1]
+    gdb_test "whatis ($type) 0" " = $type"
+    gdb_test "ptype ($type) 0" " = $ptype"
+}
+
+# Test:
+# - whatis/ptype of variables of typedef type.
+# - whatis/ptype of typedef type names.
+# - whatis/ptype of typedef-of-typedef type names.
+
+# Columns in the sublists represent:
+    # Type name    # whatis      # ptype
+foreach elem {
+    {"v_d32_t"     "d32_t"       "_Decimal32"}
+    {"v_d64_t"     "d64_t"       "_Decimal64"}
+    {"v_d128_t"    "d128_t"      "_Decimal128"}
+
+    {"d32_t"       "_Decimal32"  "_Decimal32"}
+    {"d64_t"       "_Decimal64"  "_Decimal64"}
+    {"d128_t"      "_Decimal128" "_Decimal128"}
+
+    {"d32_t2"      "d32_t"       "_Decimal32"}
+    {"d64_t2"      "d64_t"       "_Decimal64"}
+    {"d128_t2"     "d128_t"      "_Decimal128"}
+} {
+    set type [lindex $elem 0]
+    set whatis [lindex $elem 1]
+    set ptype [lindex $elem 2]
+    gdb_test "whatis $type" " = $whatis"
+    gdb_test "ptype $type" " = $ptype"
+}
diff --git a/gdb/testsuite/gdb.base/gnu_vector.exp b/gdb/testsuite/gdb.base/gnu_vector.exp
index 44b1405..dac1714 100644
--- a/gdb/testsuite/gdb.base/gnu_vector.exp
+++ b/gdb/testsuite/gdb.base/gnu_vector.exp
@@ -95,6 +95,17 @@ gdb_test "print -f4a" "\\\$$decimal = \\{-2, -4, -8, -16\\}"
 gdb_test "print (char4) 0x01010101" "\\\$$decimal = \\{1, 1, 1, 1\\}"
 gdb_test "print (int2) lla" "\\\$$decimal = \\{1, 1\\}"
 
+# Check that "whatis" doesn't peel off the destination type's typedef
+# by mistake, in expressions that involve a cast to typedef type.
+gdb_test "whatis (char4) 0x01010101" "type = char4"
+gdb_test "whatis (int2) lla" "type = int2"
+# Check that OTOH "ptype" does peel off the destination type's
+# typedef.
+gdb_test "ptype (char4) 0x01010101" \
+    "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "ptype (int2) lla" \
+    "type = int __attribute__ \\(\\(vector_size\\(2\\)\\)\\)"
+
 if { ![string compare $endian big] } then {
     gdb_test "print (char4) ia" "\\\$$decimal = \\{0, 0, 0, 2\\}"
 } else {
@@ -167,16 +178,30 @@ gdb_test "print (double2) f2" "Cannot convert between vector values of different
 gdb_test "print (int4) c4" "Cannot convert between vector values of different sizes"
 gdb_test "print (char4) i4a" "Cannot convert between vector values of different sizes"
 
-# Test ptype on vector types.
+# Test ptype/whatis on vector types/vars.
 gdb_test "ptype c4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis c4" "type = char4"
+
 gdb_test "ptype char4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis char4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+
 gdb_test "ptype i4a" "type = int __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis i4a" "type = int4"
+
 gdb_test "ptype int4" "type = int __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis int4" "type = int __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+
 gdb_test "ptype f4b" "type = float __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis f4b" "type = float4"
+
 gdb_test "ptype float4" "type = float __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
+gdb_test "whatis float4" "type = float __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
 
 gdb_test "ptype union_with_vector_1" "type = union {\r\n\[\t \]+int i;\r\n\[\t \]+char cv __attribute__ \\(\\(vector_size\\(4\\)\\)\\);\r\n}"
+gdb_test "whatis union_with_vector_1" {type = union {...}}
+
 gdb_test "ptype struct_with_vector_1" "type = struct {\r\n\[\t \]+int i;\r\n\[\t \]+char cv __attribute__ \\(\\(vector_size\\(4\\)\\)\\);\r\n\[\t \]+float4 f4;\r\n}"
+gdb_test "whatis struct_with_vector_1" {type = struct {...}}
 
 # Test inferior function calls with vector arguments and/or vector
 # return values.
diff --git a/gdb/testsuite/gdb.base/whatis-ptype-typedefs.c b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.c
new file mode 100644
index 0000000..5711a96
--- /dev/null
+++ b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.c
@@ -0,0 +1,143 @@
+/* This test program is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Define typedefs of different types, for testing the "whatis" and
+   "ptype" commands.  */
+
+/* Helper macro used to consistently define variables/typedefs using
+   the same name scheme.  BASE is the shared part of the name of all
+   typedefs/variables generated.  Defines a variable of the given
+   typedef type, and then a typedef of that typedef and a variable of
+   that new typedef type.  The "double typedef" is useful to checking
+   that whatis only strips one typedef level.  For example, if BASE is
+   "int", we get:
+
+  int_typedef v_int_typedef; // "v_" stands for variable of typedef type
+  typedef int_typedef int_typedef2; // typedef-of-typedef
+  int_typedef2 v_int_typedef2; // var of typedef-of-typedef
+*/
+#define DEF(base)				\
+  base ## _typedef v_ ## base ## _typedef;	\
+						\
+  typedef base ## _typedef base ## _typedef2;	\
+  base ## _typedef2 v_ ## base ## _typedef2
+
+/* Void.  */
+
+/* (Can't have variables of void type.)  */
+
+typedef void void_typedef;
+typedef void_typedef void_typedef2;
+
+void_typedef *v_void_typedef_ptr;
+void_typedef2 *v_void_typedef_ptr2;
+
+/* Integers.  */
+
+typedef int int_typedef;
+DEF (int);
+
+/* Floats.  */
+
+typedef float float_typedef;
+DEF (float);
+
+/* Enums.  */
+
+typedef enum colors {red, green, blue} colors_typedef;
+DEF (colors);
+
+/* Structures.  */
+
+typedef struct t_struct
+{
+  int member;
+} t_struct_typedef;
+DEF (t_struct);
+
+/* Unions.  */
+
+typedef union t_union
+{
+  int member;
+} t_union_typedef;
+DEF (t_union);
+
+/* Arrays.  */
+
+typedef int int_array_typedef[3];
+DEF (int_array);
+
+/* An array the same size of t_struct_typedef, so we can test casting.  */
+typedef unsigned char uchar_array_t_struct_typedef[sizeof (t_struct_typedef)];
+DEF (uchar_array_t_struct);
+
+/* A struct and a eunion the same size as t_struct, so we can test
+   casting.  */
+
+typedef struct t_struct_wrapper
+{
+  struct t_struct base;
+} t_struct_wrapper_typedef;
+DEF (t_struct_wrapper);
+
+typedef union t_struct_union_wrapper
+{
+  struct t_struct base;
+} t_struct_union_wrapper_typedef;
+DEF (t_struct_union_wrapper);
+
+/* Functions / function pointers.  */
+
+typedef void func_ftype (void);
+func_ftype *v_func_ftype;
+
+typedef func_ftype func_ftype2;
+func_ftype2 *v_func_ftype2;
+
+/* C++ methods / method pointers.  */
+
+#ifdef __cplusplus
+
+namespace ns {
+
+struct Struct { void method (); };
+void Struct::method () {}
+
+typedef Struct Struct_typedef;
+DEF (Struct);
+
+/* Typedefs/vars in a namespace.  */
+typedef void (Struct::*method_ptr_typedef) ();
+DEF (method_ptr);
+
+}
+
+/* Similar, but in the global namespace.  */
+typedef ns::Struct ns_Struct_typedef;
+DEF (ns_Struct);
+
+typedef void (ns::Struct::*ns_method_ptr_typedef) ();
+DEF (ns_method_ptr);
+
+#endif
+
+int
+main (void)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp
new file mode 100644
index 0000000..d333d81
--- /dev/null
+++ b/gdb/testsuite/gdb.base/whatis-ptype-typedefs.exp
@@ -0,0 +1,272 @@
+# Copyright 2017 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test "whatis"/"ptype" of different typedef types, and of expressions
+# involving casts to/from different typedefs.
+#
+# Particularly, when "whatis" is given a type name directly, it should
+# strip one (and only one) typedef level.  Otherwise, it should not
+# strip any typedef at all.  GDB used to incorrectly strip typedefs of
+# expressions involving casts to typedef types.  E.g., (gdb) print
+# (int_typedef)0" shall result in a value of type "int_typedef", not
+# "int".
+
+standard_testfile
+
+# Prepare for testing in language LANG.  Lang can be "c" or "c++".
+
+proc prepare {lang} {
+    global srcfile testfile
+
+    if [target_info exists no_long_long] {
+	set options [list debug additional_flags=-DNO_LONG_LONG]
+    } else {
+	set options [list debug]
+    }
+
+    if {$lang == "c++"} {
+	lappend options c++
+	set out $testfile-cxx
+    } else {
+	set out $testfile-c
+    }
+
+    if { [prepare_for_testing "failed to prepare" \
+	      ${out} [list $srcfile] $options] } {
+	return -1
+    }
+
+    if ![runto_main] then {
+	fail "can't run to main"
+	return 0
+    }
+}
+
+# The following list is layed out as a table.  It is composed by
+# sub-lists (lines), with each line representing one whatis/ptype
+# test.  The sub-list (line) elements (columns) are (in order):
+#
+# EXP - The user expression passed to whatis/ptype.
+#
+# WHATIS - What "whatis" should print.
+#
+# If the EXP column is a type name, then this will be the same type,
+# with one (and only one) typedef level removed.  Otherwise, this is
+# the type of the expression on the first column, with all typedefs
+# preserved.
+#
+# PTYPE - What "ptype" should print.
+#
+# This is always the type of the input type/expression stripped from
+# all typedefs.
+#
+# LANGUAGE - If the line is language-specific, which language.
+#
+# This can be "c" or "c++".
+#
+# Columns in the table represent:
+     # EXP                # whatis           # ptype           # language
+set table {
+    {"void_typedef"       "void"              "void"}
+    {"void_typedef2"      "void_typedef"      "void"}
+
+    {"int_typedef"        "int"              "int"}
+    {"int_typedef2"       "int_typedef"      "int"}
+    {"v_int_typedef"      "int_typedef"      "int"}
+    {"v_int_typedef2"     "int_typedef2"     "int"}
+
+    {"float_typedef"      "float"            "float"}
+    {"float_typedef2"     "float_typedef"    "float"}
+    {"v_float_typedef"    "float_typedef"    "float"}
+    {"v_float_typedef2"   "float_typedef2"   "float"}
+
+    {"colors_typedef"     "(enum )?colors"   "enum colors( : unsigned int)? {red, green, blue}"}
+    {"colors_typedef2"    "colors_typedef"   "enum colors( : unsigned int)? {red, green, blue}"}
+    {"v_colors_typedef"   "colors_typedef"   "enum colors( : unsigned int)? {red, green, blue}"}
+    {"v_colors_typedef2"  "colors_typedef2"  "enum colors( : unsigned int)? {red, green, blue}"}
+
+    {"func_ftype"         "void \\(void\\)"  "void \\(void\\)"}
+    {"func_ftype2"        "func_ftype"       "void \\(void\\)"}
+
+    {"func_ftype *"       "func_ftype \\*"   "void \\(\\*\\)\\(void\\)"}
+    {"func_ftype2 *"      "func_ftype2 \\*"  "void \\(\\*\\)\\(void\\)"}
+    {"v_func_ftype"       "func_ftype \\*"   "void \\(\\*\\)\\(void\\)"}
+    {"v_func_ftype2"      "func_ftype2 \\*"  "void \\(\\*\\)\\(void\\)"}
+
+    {"v_t_struct_typedef"                "t_struct_typedef"                "struct t_struct {.* member;.*}"}
+    {"v_t_struct_typedef2"               "t_struct_typedef2"               "struct t_struct {.* member;.*}"}
+    {"v_t_struct_union_wrapper_typedef"  "t_struct_union_wrapper_typedef"  "union t_struct_union_wrapper {.*base;.*}"}
+    {"v_t_struct_union_wrapper_typedef2" "t_struct_union_wrapper_typedef2" "union t_struct_union_wrapper {.*base;.*}"}
+    {"v_uchar_array_t_struct_typedef"    "uchar_array_t_struct_typedef"    "unsigned char \\[.*\\]"}
+    {"v_uchar_array_t_struct_typedef2"   "uchar_array_t_struct_typedef2"   "unsigned char \\[.*\\]"}
+
+    {"v_ns_Struct_typedef"               "ns_Struct_typedef"                "struct ns::Struct {.* method.*}"   "c++"}
+
+    {"ns_method_ptr_typedef"
+	"void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+	"void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+	"c++"}
+
+    {"ns::method_ptr_typedef"
+	"void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+	"void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+	"c++"}
+
+    {"ns_method_ptr_typedef2"
+	"ns_method_ptr_typedef"
+	"void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+	"c++"}
+
+    {"ns::method_ptr_typedef2"
+	"ns::method_ptr_typedef"
+	"void \\(ns::Struct::\\*\\)\\(ns::Struct \\* const\\)"
+	"c++"}
+
+    {"ns::Struct::method"
+	"void \\(ns::Struct \\* const\\)"
+	"void \\(ns::Struct \\* const\\)"
+	"c++"}
+}
+
+# The 4th column above is optional.  If present, it indicates that the
+# line should only be tested in the specified language.  This is a
+# helper function that checks whether LINE's language matches LANG.
+proc line_lang_match {line lang} {
+    if {[llength $line] <= 3} {
+	return true
+    }
+
+    set line_lang [lindex $line 3]
+    if {$line_lang == "" || $lang == $line_lang} {
+	return true
+    }
+
+    return false
+}
+
+# Run tests in language LANG.
+
+proc run_tests {lang} {
+    global table
+    global gdb_prompt
+
+    # Test passing all EXP in the list/table above to whatis/ptype,
+    # and check what comes out.
+    with_test_prefix "whatis/ptype" {
+	foreach line $table {
+	    set type [lindex $line 0]
+	    set whatis [lindex $line 1]
+	    set ptype [lindex $line 2]
+
+	    if {![line_lang_match $line $lang]} {
+		continue
+	    }
+
+	    # GCC doesn't record the target type of "typedef of
+	    # typedef of void" types in the DWARF.  See
+	    # <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81267>.
+	    # Handle that case manually in order to be able to xfail
+	    # it.
+	    if {$type == "void_typedef2"} {
+		set test "whatis $type"
+		gdb_test_multiple $test $test {
+		    -re "type = void\r\n$gdb_prompt $" {
+			# gcc/81267.
+			setup_xfail "*-*-*"
+			fail "$test (void)"
+		    }
+		    -re "type = void_typedef\r\n$gdb_prompt $" {
+			pass $test
+		    }
+		}
+	    } else {
+		gdb_test "whatis $type" "type = $whatis"
+	    }
+
+	    gdb_test "ptype $type" "type = $ptype"
+	}
+    }
+
+    # Test converting/casting all variables in the first column of the
+    # table to all types (found in the first column of the table).
+    # The aggregates are all defined to be the same size so that
+    # casting actually works.  (GDB's casting operator is more general
+    # than a C cast.)
+    #
+    # The main idea here is testing all the different paths in the
+    # value casting code in GDB (value_cast), making sure typedefs are
+    # preserved.
+    with_test_prefix "cast" {
+	foreach line1 $table {
+	    set from [lindex $line1 0]
+
+	    if {![line_lang_match $line1 $lang]} {
+		continue
+	    }
+
+	    foreach line2 $table {
+		set to [lindex $line2 0]
+		set whatis [lindex $line2 1]
+		set ptype [lindex $line2 2]
+
+		if {![line_lang_match $line2 $lang]} {
+		    continue
+		}
+
+		# We try all combinations, even those that don't
+		# parse, or are invalid, to catch the case of a
+		# regression making them inadvertently valid.  For
+		# example, these convertions are invalid:
+		#
+		#  float <-> array
+		#  array -> function (not function pointer)
+		#  array -> member_ptr
+		#
+		# while these are invalid syntax:
+		#
+		#  (anything) type
+		#  (var) anything
+		#  (method) anything   [not method pointer]
+		#  (float) method
+		#
+		if {([string match "v_*" $to]
+		     || (![string match "v_*" $from] && ![string match "*method" $from])
+		     || [string match "*method" $to])} {
+		    gdb_test "whatis ($to) $from" "syntax error.*" "whatis ($to) $from (syntax)"
+		    gdb_test "ptype ($to) $from" "syntax error.*" "ptype ($to) $from (syntax)"
+		} elseif {([string match "*float*" $from] && [string match "*array*" $to])
+			  || ([string match "float*" $to] && [string match "*array*" $from])
+			  || ([string match "float*" $to] && [string match "*method" $from])
+			  || ([string match "*ftype" $to] && [string match "*array*" $from])
+			  || ([string match "*ftype2" $to] && [string match "*array*" $from])
+			  || ([string match "*ftype" $to] && [string match "*method" $from])
+			  || ([string match "*ftype2" $to] && [string match "*method" $from])
+			  || ([string match "*method_ptr*" $to] && [string match "*method" $from])
+			  || ([string match "*method_ptr*" $to] && [string match "*array*" $from])} {
+		    gdb_test "whatis ($to) $from" "Invalid cast." "whatis ($to) $from (invalid)"
+		    gdb_test "ptype ($to) $from" "Invalid cast." "ptype ($to) $from (invalid)"
+		} else {
+		    gdb_test "whatis ($to) $from" "type = [string_to_regexp $to]"
+		    gdb_test "ptype ($to) $from" "type = $ptype"
+		}
+	    }
+	}
+    }
+}
+
+foreach_with_prefix lang {"c" "c++"} {
+    prepare $lang
+    run_tests $lang
+}
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index fc687d3..045271a 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -443,12 +443,40 @@ whatis_exp (char *exp, int show)
 	}
 
       expression_up expr = parse_expression (exp);
-      val = evaluate_type (expr.get ());
+
+      /* The behavior of "whatis" depends on whether the user
+	 expression names a type directly, or a language expression
+	 (including variable names).  If the former, then "whatis"
+	 strips one level of typedefs, only.  If an expression,
+	 "whatis" prints the type of the expression without stripping
+	 any typedef level.  "ptype" always strips all levels of
+	 typedefs.  */
+      if (show == -1 && expr->elts[0].opcode == OP_TYPE)
+	{
+	  /* The user expression names a type directly.  */
+	  type = expr->elts[1].type;
+
+	  /* If this is a typedef, then find its immediate target.
+	     Use check_typedef to resolve stubs, but ignore its result
+	     because we do not want to dig past all typedefs.  */
+	  check_typedef (type);
+	  if (TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
+	    type = TYPE_TARGET_TYPE (type);
+	}
+      else
+	{
+	  /* The user expression names a type indirectly by naming an
+	     object or expression of that type.  Find that
+	     indirectly-named type.  */
+	  val = evaluate_type (expr.get ());
+	  type = value_type (val);
+	}
     }
   else
-    val = access_value_history (0);
-
-  type = value_type (val);
+    {
+      val = access_value_history (0);
+      type = value_type (val);
+    }
 
   get_user_print_options (&opts);
   if (opts.objectprint)
diff --git a/gdb/valops.c b/gdb/valops.c
index 8675e6c..f3f5308 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -378,6 +378,11 @@ value_cast (struct type *type, struct value *arg2)
     /* We deref the value and then do the cast.  */
     return value_cast (type, coerce_ref (arg2)); 
 
+  /* Strip typedefs / resolve stubs in order to get at the type's
+     code/length, but remember the original type, to use as the
+     resulting type of the cast, in case it was a typedef.  */
+  struct type *to_type = type;
+
   type = check_typedef (type);
   code1 = TYPE_CODE (type);
   arg2 = coerce_ref (arg2);
@@ -433,7 +438,7 @@ value_cast (struct type *type, struct value *arg2)
   code2 = TYPE_CODE (type2);
 
   if (code1 == TYPE_CODE_COMPLEX)
-    return cast_into_complex (type, arg2);
+    return cast_into_complex (to_type, arg2);
   if (code1 == TYPE_CODE_BOOL)
     {
       code1 = TYPE_CODE_INT;
@@ -452,14 +457,14 @@ value_cast (struct type *type, struct value *arg2)
       && (code2 == TYPE_CODE_STRUCT || code2 == TYPE_CODE_UNION)
       && TYPE_NAME (type) != 0)
     {
-      struct value *v = value_cast_structs (type, arg2);
+      struct value *v = value_cast_structs (to_type, arg2);
 
       if (v)
 	return v;
     }
 
   if (code1 == TYPE_CODE_FLT && scalar)
-    return value_from_double (type, value_as_double (arg2));
+    return value_from_double (to_type, value_as_double (arg2));
   else if (code1 == TYPE_CODE_DECFLOAT && scalar)
     {
       enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type));
@@ -475,7 +480,7 @@ value_cast (struct type *type, struct value *arg2)
 	/* The only option left is an integral type.  */
 	decimal_from_integral (arg2, dec, dec_len, byte_order);
 
-      return value_from_decfloat (type, dec);
+      return value_from_decfloat (to_type, dec);
     }
   else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM
 	    || code1 == TYPE_CODE_RANGE)
@@ -496,7 +501,7 @@ value_cast (struct type *type, struct value *arg2)
 		     gdbarch_byte_order (get_type_arch (type2)));
       else
         longest = value_as_long (arg2);
-      return value_from_longest (type, convert_to_boolean ?
+      return value_from_longest (to_type, convert_to_boolean ?
 				 (LONGEST) (longest ? 1 : 0) : longest);
     }
   else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT  
@@ -522,14 +527,14 @@ value_cast (struct type *type, struct value *arg2)
 	      || longest <= -((LONGEST) 1 << addr_bit))
 	    warning (_("value truncated"));
 	}
-      return value_from_longest (type, longest);
+      return value_from_longest (to_type, longest);
     }
   else if (code1 == TYPE_CODE_METHODPTR && code2 == TYPE_CODE_INT
 	   && value_as_long (arg2) == 0)
     {
-      struct value *result = allocate_value (type);
+      struct value *result = allocate_value (to_type);
 
-      cplus_make_method_ptr (type, value_contents_writeable (result), 0, 0);
+      cplus_make_method_ptr (to_type, value_contents_writeable (result), 0, 0);
       return result;
     }
   else if (code1 == TYPE_CODE_MEMBERPTR && code2 == TYPE_CODE_INT
@@ -537,7 +542,7 @@ value_cast (struct type *type, struct value *arg2)
     {
       /* The Itanium C++ ABI represents NULL pointers to members as
 	 minus one, instead of biasing the normal case.  */
-      return value_from_longest (type, -1);
+      return value_from_longest (to_type, -1);
     }
   else if (code1 == TYPE_CODE_ARRAY && TYPE_VECTOR (type)
 	   && code2 == TYPE_CODE_ARRAY && TYPE_VECTOR (type2)
@@ -548,21 +553,21 @@ value_cast (struct type *type, struct value *arg2)
     error (_("can only cast scalar to vector of same size"));
   else if (code1 == TYPE_CODE_VOID)
     {
-      return value_zero (type, not_lval);
+      return value_zero (to_type, not_lval);
     }
   else if (TYPE_LENGTH (type) == TYPE_LENGTH (type2))
     {
       if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
-	return value_cast_pointers (type, arg2, 0);
+	return value_cast_pointers (to_type, arg2, 0);
 
       arg2 = value_copy (arg2);
-      deprecated_set_value_type (arg2, type);
-      set_value_enclosing_type (arg2, type);
+      deprecated_set_value_type (arg2, to_type);
+      set_value_enclosing_type (arg2, to_type);
       set_value_pointed_to_offset (arg2, 0);	/* pai: chk_val */
       return arg2;
     }
   else if (VALUE_LVAL (arg2) == lval_memory)
-    return value_at_lazy (type, value_address (arg2));
+    return value_at_lazy (to_type, value_address (arg2));
   else
     {
       error (_("Invalid cast."));
