diff --git a/gcc/value-range-equiv.cc b/gcc/value-range-equiv.cc
index 77c6f5ca99d..b0ae1288a09 100644
--- a/gcc/value-range-equiv.cc
+++ b/gcc/value-range-equiv.cc
@@ -50,6 +50,12 @@ value_range_equiv::set (tree min, tree max, bitmap equiv,
     check ();
 }
 
+void
+value_range_equiv::set (tree min, tree max, value_range_kind kind)
+{
+  set (min, max, m_equiv, kind);
+}
+
 void
 value_range_equiv::set (tree val)
 {
diff --git a/gcc/value-range-equiv.h b/gcc/value-range-equiv.h
index 0aa1069cb61..743ceb2b227 100644
--- a/gcc/value-range-equiv.h
+++ b/gcc/value-range-equiv.h
@@ -41,9 +41,10 @@ class GTY((user)) value_range_equiv : public value_range
   void move (value_range_equiv *);
 
   /* Leaves equiv bitmap alone.  */
+  virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
   void update (tree, tree, value_range_kind = VR_RANGE);
   /* Deep-copies equiv bitmap argument.  */
-  void set (tree, tree, bitmap = NULL, value_range_kind = VR_RANGE);
+  void set (tree, tree, bitmap, value_range_kind = VR_RANGE);
   void set (tree);
 
   bool operator== (const value_range_equiv &) const /* = delete */;
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 2e7385aecc2..97ff0614f48 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -30,6 +30,162 @@ along with GCC; see the file COPYING3.  If not see
 #include "fold-const.h"
 #include "gimple-range.h"
 
+// Default implementation when none has been defined.
+
+bool
+vrange::contains_p (tree) const
+{
+  return false;
+}
+
+// Default implementation when none has been defined.
+
+bool
+vrange::singleton_p (tree *) const
+{
+  return false;
+}
+
+// Assignment operator for generic ranges.  Copying incompatible types
+// is not allowed.
+
+vrange &
+vrange::operator= (const vrange &src)
+{
+  if (is_a <irange> (src))
+    {
+      as_a <irange> (*this) = as_a <irange> (src);
+      return *this;
+    }
+  else
+    gcc_unreachable ();
+}
+
+// Equality operator for generic ranges.
+
+bool
+vrange::operator== (const vrange &src) const
+{
+  if (is_a <irange> (src))
+    return as_a <irange> (*this) == as_a <irange> (src);
+  gcc_unreachable ();
+}
+
+// Return TRUE if R fits in THIS.
+
+bool
+irange::fits_p (const vrange &r) const
+{
+  return m_max_ranges >= as_a <irange> (r).num_pairs ();
+}
+
+void
+irange::set_nonnegative (tree type)
+{
+  set (build_int_cst (type, 0), TYPE_MAX_VALUE (type));
+}
+
+unsupported_range::unsupported_range ()
+{
+  m_discriminator = VR_UNKNOWN;
+  set_undefined ();
+}
+
+void
+unsupported_range::set (tree, tree, value_range_kind)
+{
+  gcc_unreachable ();
+}
+
+tree
+unsupported_range::type () const
+{
+  gcc_unreachable ();
+  return nullptr;
+}
+
+void
+unsupported_range::set_undefined ()
+{
+  m_kind = VR_UNDEFINED;
+}
+
+void
+unsupported_range::set_varying (tree)
+{
+  gcc_unreachable ();
+}
+
+void
+unsupported_range::dump (FILE *file) const
+{
+  fprintf (file, "[unsupported_range] ");
+  if (undefined_p ())
+    {
+      fprintf (file, "UNDEFINED");
+      return;
+    }
+  if (varying_p ())
+    {
+      fprintf (file, "VARYING");
+      return;
+    }
+  gcc_unreachable ();
+}
+
+bool
+unsupported_range::union_ (const vrange &)
+{
+  gcc_unreachable ();
+  return false;
+}
+
+bool
+unsupported_range::intersect (const vrange &)
+{
+  gcc_unreachable ();
+  return false;
+}
+
+bool
+unsupported_range::zero_p () const
+{
+  gcc_unreachable ();
+  return false;
+}
+
+bool
+unsupported_range::nonzero_p () const
+{
+  gcc_unreachable ();
+  return false;
+}
+
+void
+unsupported_range::set_nonzero (tree)
+{
+  gcc_unreachable ();
+}
+
+void
+unsupported_range::set_zero (tree)
+{
+  gcc_unreachable ();
+}
+
+void
+unsupported_range::set_nonnegative (tree)
+{
+  gcc_unreachable ();
+}
+
+bool
+unsupported_range::fits_p (const vrange &) const
+{
+  gcc_unreachable ();
+  return false;
+}
+
 // Here we copy between any two irange's.  The ranges can be legacy or
 // multi-ranges, and copying between any combination works correctly.
 
@@ -291,7 +447,7 @@ irange::set (tree min, tree max, value_range_kind kind)
     }
   if (kind == VR_UNDEFINED)
     {
-      set_undefined ();
+      irange::set_undefined ();
       return;
     }
 
@@ -370,6 +526,7 @@ irange::set (tree min, tree max, value_range_kind kind)
 void
 irange::verify_range ()
 {
+  gcc_checking_assert (m_discriminator == VR_IRANGE);
   if (m_kind == VR_UNDEFINED)
     {
       gcc_checking_assert (m_num_ranges == 0);
@@ -2087,6 +2244,7 @@ dump_bound_with_infinite_markers (FILE *file, tree bound)
 void
 irange::dump (FILE *file) const
 {
+  fprintf (file, "[irange] ");
   if (undefined_p ())
     {
       fprintf (file, "UNDEFINED");
@@ -2121,27 +2279,27 @@ irange::dump (FILE *file) const
 }
 
 void
-irange::debug () const
+vrange::debug () const
 {
   dump (stderr);
   fprintf (stderr, "\n");
 }
 
 void
-dump_value_range (FILE *file, const irange *vr)
+dump_value_range (FILE *file, const vrange *vr)
 {
   vr->dump (file);
 }
 
 DEBUG_FUNCTION void
-debug (const irange *vr)
+debug (const vrange *vr)
 {
   dump_value_range (stderr, vr);
   fprintf (stderr, "\n");
 }
 
 DEBUG_FUNCTION void
-debug (const irange &vr)
+debug (const vrange &vr)
 {
   debug (&vr);
 }
diff --git a/gcc/value-range.h b/gcc/value-range.h
index ec59d2e4f23..0061f667092 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_VALUE_RANGE_H
 #define GCC_VALUE_RANGE_H
 
+class irange;
+
 // Types of value ranges.
 enum value_range_kind
 {
@@ -37,24 +39,71 @@ enum value_range_kind
   VR_LAST
 };
 
-// Range of values that can be associated with an SSA_NAME.
-//
-// This is the base class without any storage.
+// Discriminator between different vrange types.
+
+enum value_range_discriminator
+{
+  // Range holds an integer or pointer.
+  VR_IRANGE,
+  // Range holds an unsupported type.
+  VR_UNKNOWN
+};
+
+// Abstract class for ranges of any of the supported types.
+
+class vrange
+{
+  template <typename T> friend bool is_a (vrange &);
+public:
+  virtual void set (tree, tree, value_range_kind = VR_RANGE) = 0;
+  virtual tree type () const = 0;
+  virtual void set_varying (tree type) = 0;
+  virtual void set_undefined () = 0;
+  virtual void dump (FILE * = stderr) const = 0;
+  virtual bool union_ (const vrange &) = 0;
+  virtual bool intersect (const vrange &) = 0;
+  virtual bool singleton_p (tree *result = NULL) const;
+  virtual bool contains_p (tree cst) const;
+  virtual bool zero_p () const = 0;
+  virtual bool nonzero_p () const = 0;
+  virtual void set_nonzero (tree type) = 0;
+  virtual void set_zero (tree type) = 0;
+  virtual void set_nonnegative (tree type) = 0;
+  virtual bool fits_p (const vrange &r) const = 0;
+
+  static bool supports_type_p (tree);
+
+  bool varying_p () const;
+  bool undefined_p () const;
+  vrange& operator= (const vrange &);
+  bool operator== (const vrange &) const;
+  bool operator!= (const vrange &r) const { return !(*this == r); }
+
+  enum value_range_kind kind () const;		// DEPRECATED
+  void debug () const;
+
+protected:
+  ENUM_BITFIELD(value_range_kind) m_kind : 8;
+  ENUM_BITFIELD(value_range_discriminator) m_discriminator : 4;
+};
+
+// An integer range without any storage.
 
-class GTY((user)) irange
+class GTY((user)) irange : public vrange
 {
   friend class irange_allocator;
 public:
   // In-place setters.
-  void set (tree, tree, value_range_kind = VR_RANGE);
-  void set_nonzero (tree);
-  void set_zero (tree);
-  void set_varying (tree type);
-  void set_undefined ();
+  virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+  virtual void set_nonzero (tree type) override;
+  virtual void set_zero (tree type) override;
+  virtual void set_nonnegative (tree type) override;
+  virtual void set_varying (tree type) override;
+  virtual void set_undefined () override;
 
   // Range types.
   static bool supports_type_p (tree);
-  tree type () const;
+  virtual tree type () const override;
 
   // Iteration over sub-ranges.
   unsigned num_pairs () const;
@@ -63,16 +112,14 @@ public:
   wide_int upper_bound () const;
 
   // Predicates.
-  bool zero_p () const;
-  bool nonzero_p () const;
-  bool undefined_p () const;
-  bool varying_p () const;
-  bool singleton_p (tree *result = NULL) const;
-  bool contains_p (tree) const;
+  virtual bool zero_p () const override;
+  virtual bool nonzero_p () const override;
+  virtual bool singleton_p (tree *result = NULL) const override;
+  virtual bool contains_p (tree cst) const override;
 
   // In-place operators.
-  bool union_ (const irange &);
-  bool intersect (const irange &);
+  virtual bool union_ (const vrange &) override;
+  virtual bool intersect (const vrange &) override;
   void invert ();
 
   // Operator overloads.
@@ -81,12 +128,10 @@ public:
   bool operator!= (const irange &r) const { return !(*this == r); }
 
   // Misc methods.
-  bool fits_p (const irange &r) { return m_max_ranges >= r.num_pairs (); }
-  void dump (FILE * = stderr) const;
-  void debug () const;
+  virtual bool fits_p (const vrange &r) const override;
+  virtual void dump (FILE * = stderr) const override;
 
   // Deprecated legacy public methods.
-  enum value_range_kind kind () const;		// DEPRECATED
   tree min () const;				// DEPRECATED
   tree max () const;				// DEPRECATED
   bool symbolic_p () const;			// DEPRECATED
@@ -139,7 +184,6 @@ private:
   bool intersect (const wide_int& lb, const wide_int& ub);
   unsigned char m_num_ranges;
   unsigned char m_max_ranges;
-  ENUM_BITFIELD(value_range_kind) m_kind : 8;
   tree *m_base;
 };
 
@@ -173,6 +217,88 @@ private:
   tree m_ranges[N*2];
 };
 
+// Unsupported temporaries may be created by ranger before it's known
+// they're unsupported, or by vr_values::get_value_range.  All
+// operations except construction cause a trap.
+
+class unsupported_range : public vrange
+{
+public:
+  unsupported_range ();
+  virtual void set (tree, tree, value_range_kind) override;
+  virtual tree type () const override;
+  virtual void set_varying (tree type) override;
+  virtual void set_undefined () override;
+  virtual void dump (FILE *) const override;
+  virtual bool union_ (const vrange &) override;
+  virtual bool intersect (const vrange &) override;
+  virtual bool zero_p () const override;
+  virtual bool nonzero_p () const override;
+  virtual void set_nonzero (tree) override;
+  virtual void set_zero (tree) override;
+  virtual void set_nonnegative (tree) override;
+  virtual bool fits_p (const vrange &) const override;
+};
+
+// Traits to implement vrange is_a<> and as_a<>.
+
+template<typename T>
+struct vrange_traits
+{
+  // Default to something unusable.
+  typedef void range_type;
+};
+
+template<>
+struct vrange_traits<irange>
+{
+  typedef irange range_type;
+};
+
+template <typename T>
+inline bool
+is_a (vrange &v)
+{
+  gcc_unreachable ();
+  return false;
+}
+
+template <typename T>
+inline bool
+is_a (const vrange &v)
+{
+  // Reuse is_a <vrange> to implement the const version.
+  const T &derived = static_cast<const T &> (v);
+  return is_a <T> (const_cast<T &> (derived));
+}
+
+template <typename T>
+inline T &
+as_a (vrange &v)
+{
+  typedef typename vrange_traits<T>::range_type range_type;
+  gcc_checking_assert (is_a <range_type> (v));
+  return static_cast <range_type &> (v);
+}
+
+template <typename T>
+inline const T &
+as_a (const vrange &v)
+{
+  typedef typename vrange_traits<T>::range_type range_type;
+  gcc_checking_assert (is_a <range_type> (v));
+  return static_cast <const range_type &> (v);
+}
+
+// Specializations for the different range types.
+
+template <>
+inline bool
+is_a <irange> (vrange &v)
+{
+  return v.m_discriminator == VR_IRANGE;
+}
+
 // This is a special int_range<1> with only one pair, plus
 // VR_ANTI_RANGE magic to describe slightly more than can be described
 // in one pair.  It is described in the code as a "legacy range" (as
@@ -197,13 +323,13 @@ irange::legacy_mode_p () const
 extern bool range_has_numeric_bounds_p (const irange *);
 extern bool ranges_from_anti_range (const value_range *,
 				    value_range *, value_range *);
-extern void dump_value_range (FILE *, const irange *);
+extern void dump_value_range (FILE *, const vrange *);
 extern bool vrp_val_is_min (const_tree);
 extern bool vrp_val_is_max (const_tree);
 extern bool vrp_operand_equal_p (const_tree, const_tree);
 
 inline value_range_kind
-irange::kind () const
+vrange::kind () const
 {
   return m_kind;
 }
@@ -293,13 +419,13 @@ irange::varying_compatible_p () const
 }
 
 inline bool
-irange::varying_p () const
+vrange::varying_p () const
 {
   return m_kind == VR_VARYING;
 }
 
 inline bool
-irange::undefined_p () const
+vrange::undefined_p () const
 {
   return m_kind == VR_UNDEFINED;
 }
@@ -398,6 +524,7 @@ gt_pch_nx (int_range<N> *x, gt_pointer_operator op, void *cookie)
 inline
 irange::irange (tree *base, unsigned nranges)
 {
+  m_discriminator = VR_IRANGE;
   m_base = base;
   m_max_ranges = nranges;
   set_undefined ();
@@ -547,21 +674,21 @@ irange::upper_bound () const
 }
 
 inline bool
-irange::union_ (const irange &r)
+irange::union_ (const vrange &r)
 {
   dump_flags_t m_flags = dump_flags;
   dump_flags &= ~TDF_DETAILS;
-  bool ret = irange::legacy_verbose_union_ (&r);
+  bool ret = irange::legacy_verbose_union_ (&as_a <irange> (r));
   dump_flags = m_flags;
   return ret;
 }
 
 inline bool
-irange::intersect (const irange &r)
+irange::intersect (const vrange &r)
 {
   dump_flags_t m_flags = dump_flags;
   dump_flags &= ~TDF_DETAILS;
-  bool ret = irange::legacy_verbose_intersect (&r);
+  bool ret = irange::legacy_verbose_intersect (&as_a <irange> (r));
   dump_flags = m_flags;
   return ret;
 }
@@ -608,6 +735,12 @@ irange::normalize_kind ()
     }
 }
 
+inline bool
+vrange::supports_type_p (tree type)
+{
+  return irange::supports_type_p (type);
+}
+
 // Return the maximum value for TYPE.
 
 inline tree
diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc
index 47faa4ff938..6f8583c8d01 100644
--- a/gcc/vr-values.cc
+++ b/gcc/vr-values.cc
@@ -883,7 +883,7 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr,
 	      wide_int wmax = wi::to_wide (max, TYPE_PRECISION (TREE_TYPE (max)));
 	      tree range_min = build_zero_cst (expr_type);
 	      tree range_max = wide_int_to_tree (expr_type, wmax - 1);
-	      vr->set (range_min, range_max);
+	      vr->set (range_min, range_max, NULL);
 	      return;
 	    }
      }
@@ -1275,7 +1275,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
 		      /* This is the boolean return value whether compare and
 			 exchange changed anything or not.  */
 		      vr->set (build_int_cst (type, 0),
-			       build_int_cst (type, 1));
+			       build_int_cst (type, 1), NULL);
 		      return;
 		    }
 		  break;
@@ -1297,7 +1297,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
 			vr->set_varying (type);
 		      else
 			vr->set (build_int_cst (type, 0),
-				 build_int_cst (type, 1));
+				 build_int_cst (type, 1), NULL);
 		    }
 		  else if (types_compatible_p (type, TREE_TYPE (op0))
 			   && types_compatible_p (type, TREE_TYPE (op1)))
