[gcjx] Patch: FYI: emit casts for erased differences

Tom Tromey tromey@redhat.com
Sat Dec 3 00:28:00 GMT 2005


I'm checking this in on the gcjx branch.

Consider this code:

    class rvalcontainer<T> {
      public T field;
      public T method() { return null; }
    }

    public class rval {
      public static String mf(rvalcontainer<String> x) {
        return x.field;
      }

      public static String mm(rvalcontainer<String> x) {
        return x.method();
      }
    }

Here we must emit an explicit checkcast in both the methods of rval,
because the erased type of the member in use (e.g., 'field') differs
from the actual type.

This patch implements this.

Currently, with a few dumb workarounds (one in classpath, two ugly
ones in gcjx), I can build the classpath generics branch with gcjx.
There's still a lot to do, but this is a nice milestone nevertheless.

Tom

Index: ChangeLog
from  Tom Tromey  <tromey@redhat.com>
	* model/field.cc (model_field): Initialize new field.
	(model_field): Added 'other' argument.
	(apply_type_map): Updated.
	(erasure): Updated.  Delegate to parent if one exists.
	* model/field.hh (model_field::parent): New field.
	(model_field): New constructor.
	* model/method.cc (model_method): Initialize new field.
	(erasure): Delegate to parent, if it exists.
	(get_erased_return_type): New method.
	* model/method.hh (model_method::parent): New field.
	(model_method): Initialize new field.
	(model_method::get_erased_return_type): Declare.
	* bytecode/generate.hh (bytecode_generator::handle_invocation):
	Updated.
	* bytecode/generate.cc (handle_invocation): Added 'request'
	argument.  Emit cast if erasure changes method's return type.
	(emit_cast_maybe_boxing): Updated.
	(visit_field_ref): Likewise.
	(visit_method_invocation): Likewise.
	(visit_type_qualified_invocation): Likewise.
	(visit_super_invocation): Likewise.
	(visit_this_invocation): Likewise.
	(visit_field_ref): Emit cast if erasure changes field's type.

Index: bytecode/generate.hh
===================================================================
--- bytecode/generate.hh	(revision 107604)
+++ bytecode/generate.hh	(working copy)
@@ -268,7 +268,8 @@
 			const ref_expression &,
 			bytecode_block *,
 			bytecode_block *);
-  void handle_invocation (java_opcode,
+  void handle_invocation (model_element *,
+			  java_opcode,
 			  model_class *,
 			  const model_method *,
 			  const std::list<ref_expression> &,
Index: bytecode/generate.cc
===================================================================
--- bytecode/generate.cc	(revision 107604)
+++ bytecode/generate.cc	(working copy)
@@ -2281,8 +2281,8 @@
 			   assert_cast<model_class *> (expr_type),
 			   NULL, tmp_dest_type, request);
 	  std::list<ref_expression> args;
-	  handle_invocation (op_invokevirtual, call->get_declaring_class (),
-			     call, args);
+	  handle_invocation (request, op_invokevirtual,
+			     call->get_declaring_class (), call, args);
 	  if (tmp_dest_type != dest_type)
 	    {
 	      assert (tmp_dest_type == primitive_char_type);
@@ -2301,7 +2301,7 @@
 	  // We do pass an argument, but sneakily: we pushed it up
 	  // above.
 	  std::list<ref_expression> args;
-	  handle_invocation (op_invokestatic, dest_class, call, args);
+	  handle_invocation (request, op_invokestatic, dest_class, call, args);
 	  // We have to pop this ourselves due to lying to
 	  // handle_invocation.
 	  reduce_stack (expr_type);
@@ -2758,7 +2758,8 @@
       else
 	{
 	  std::list<ref_expression> nullargs;
-	  handle_invocation (op_invokestatic, ref->get_qualifying_class (),
+	  handle_invocation (ref, op_invokestatic,
+			     ref->get_qualifying_class (),
 			     tm, nullargs, false, ! field->static_p ());
 	}
     }
@@ -2772,6 +2773,11 @@
     }
   else
     {
+      // Ordinary field reference.  We take the erasure of the field
+      // to ensure that we end up using the proper type.
+      model_type *orig_type = field->type ();
+      field = assert_cast<model_field *> (field->erasure ());
+
       if (expr && expr->type ()->array_p ())
 	{
 	  assert (field->get_name () == "length");
@@ -2787,6 +2793,10 @@
 	reduce_stack (expr->type ());
       increase_stack (field->type ());
 
+      // If erasure changes the field's type, we must emit a cast.
+      if (orig_type != field->type ())
+	emit_cast (orig_type, field->type ());
+
       if (expr_target == CONDITIONAL)
 	{
 	  emit_relocation (ifne, current_true_target);
@@ -3116,7 +3126,8 @@
 }
 
 void
-bytecode_generator::handle_invocation (java_opcode opcode,
+bytecode_generator::handle_invocation (model_element *request,
+				       java_opcode opcode,
 				       model_class *qualifier,
 				       const model_method *meth,
 				       const std::list<ref_expression> &args,
@@ -3148,9 +3159,8 @@
 
   int ii_count = invokeinterface_count (args);
   if (ii_count > 255)
-    // FIXME: location should really be location of the call.
-    throw method->error ("method invocation requires more than "
-			 "255 words of method arguments");
+    throw request->error ("method invocation requires more than "
+			  "255 words of method arguments");
 
   emit (opcode);
 
@@ -3170,8 +3180,17 @@
   if (! meth->static_p () || use_accessor)
     reduce_stack (meth->get_declaring_class ());
   if (meth->get_return_type () != primitive_void_type)
-    increase_stack (meth->get_return_type ());
+    {
+      model_type *return_type = meth->get_return_type ();
+      increase_stack (return_type);
 
+      // If the method's return type changes due to erasure, we must
+      // emit a cast.
+      model_type *erased_type = meth->get_erased_return_type ();
+      if (return_type != erased_type)
+	emit_cast (return_type, erased_type);
+    }
+
   if (expr_target == IGNORE && meth->get_return_type () != primitive_void_type)
     emit_pop (meth->get_return_type ());
   else if (expr_target == CONDITIONAL)
@@ -3214,7 +3233,7 @@
     opcode = op_invokespecial;
   else
     opcode = op_invokevirtual;
-  handle_invocation (opcode, inv->get_qualifying_class (), meth, args,
+  handle_invocation (inv, opcode, inv->get_qualifying_class (), meth, args,
 		     false, nonstatic_accessor);
 }
 
@@ -3237,7 +3256,7 @@
       meth = accessed->get_accessor (const_cast<model_method *> (meth));
     }
 
-  handle_invocation (super ? op_invokespecial : op_invokestatic,
+  handle_invocation (inv, super ? op_invokespecial : op_invokestatic,
 		     inv->get_qualifying_class (),
 		     meth, args, false, nonstatic_accessor);
 }
@@ -3259,7 +3278,7 @@
     }
 
   emit_load (method->get_declaring_class (), this_index);
-  handle_invocation (op_invokespecial, inv->get_qualifying_class (),
+  handle_invocation (inv, op_invokespecial, inv->get_qualifying_class (),
 		     meth, args, inv->get_expression () != NULL,
 		     nonstatic_accessor);
 
@@ -3275,7 +3294,7 @@
 {
   // Note: an accessor can never be needed here.
   emit_load (method->get_declaring_class (), this_index);
-  handle_invocation (op_invokespecial, inv->get_qualifying_class (),
+  handle_invocation (inv, op_invokespecial, inv->get_qualifying_class (),
 		     meth, args, false, inv->get_expression () != NULL);
 }
 
Index: model/field.cc
===================================================================
--- model/field.cc	(revision 107901)
+++ model/field.cc	(working copy)
@@ -28,7 +28,8 @@
 			 vardecl->get_declared_type (),
 			 vardecl->get_declaring_class ()),
     IModifiable (),
-    state (NONE)
+    state (NONE),
+    parent (NULL)
 {
   set_initializer (vardecl->get_initializer ());
 }
@@ -38,10 +39,22 @@
 			  model_class *decl)
   : model_variable_decl (w, n, t, decl),
     IModifiable (),
-    state (NONE)
+    state (NONE),
+    parent (NULL)
 {
 }
 
+model_field::model_field (const location &w, const std::string &n,
+			  const ref_forwarding_type &t,
+			  model_field *other,
+			  model_class *decl)
+  : model_variable_decl (w, n, t, decl),
+    IModifiable (),
+    state (NONE),
+    parent (other)
+{
+}
+
 bool
 model_field::constant_p ()
 {
@@ -186,12 +199,15 @@
     return this;
   ref_forwarding_type nt
     = new model_forwarding_resolved (get_location (), param);
-  return new model_field (get_location (), name, nt, request);
+  return new model_field (get_location (), name, nt, this, request);
 }
 
 model_variable_decl *
 model_field::erasure ()
 {
+  if (parent != NULL)
+    return parent->erasure ();
+
   model_type *self_type = type ();
   if (self_type->primitive_p ())
     return this;
@@ -202,7 +218,7 @@
     return this;
   ref_forwarding_type nt
     = new model_forwarding_resolved (get_location (), param);
-  return new model_field (get_location (), name, nt,
+  return new model_field (get_location (), name, nt, this,
 			  assert_cast<model_class *> (declaring_class->erasure ()));
 }
 
Index: model/field.hh
===================================================================
--- model/field.hh	(revision 107604)
+++ model/field.hh	(working copy)
@@ -48,6 +48,10 @@
   // Indicates our current resolution state.
   resolution_state_value state;
 
+  // If this is a generic instantiation, this points to the parent
+  // field.
+  model_field *parent;
+
   void massage_modifiers (const ref_modifier_list &)
   {
     // Nothing.
@@ -61,6 +65,12 @@
   void check_serialization_fields ();
   void require_resolution ();
 
+  model_field (const location &,
+	       const std::string &,
+	       const ref_forwarding_type &,
+	       model_field *,
+	       model_class *);
+
 public:
 
   model_field (const ref_variable_decl &);
Index: model/method.cc
===================================================================
--- model/method.cc	(revision 107859)
+++ model/method.cc	(working copy)
@@ -46,7 +46,8 @@
     IModifiable (other),
     IMember (enclosing),
     state (other->state),
-    method_end (other->method_end)
+    method_end (other->method_end),
+    parent (other)
 {
   set_name (other->name);
 
@@ -88,7 +89,8 @@
     IModifiable (other),
     IMember (enclosing),
     state (other->state),
-    method_end (other->method_end)
+    method_end (other->method_end),
+    parent (other)
 {
   set_name (other->name);
 
@@ -894,6 +896,9 @@
 model_method *
 model_method::erasure (model_class *enclosing)
 {
+  if (parent)
+    return parent->erasure (enclosing);
+
   model_method *result = instance_cache.find_erased_instance ();
   if (result == NULL)
     {
@@ -903,6 +908,14 @@
   return result;
 }
 
+model_type *
+model_method::get_erased_return_type () const
+{
+  if (parent)
+    return parent->get_erased_return_type ();
+  return return_type->type ()->erasure ();
+}
+
 
 
 void
Index: model/method.hh
===================================================================
--- model/method.hh	(revision 107604)
+++ model/method.hh	(working copy)
@@ -96,6 +96,10 @@
   // this is used by GCC for debugging information.
   location method_end;
 
+  // If this is a generic instantiation, this points to the parent
+  // method.
+  model_method *parent;
+
   // All generic instantiations of this method.
   model_instance_cache<model_method> instance_cache;
 
@@ -130,7 +134,8 @@
       is_instance_initializer (false),
       state (NONE),
       // By default we set the end location to the start location.
-      method_end (w)
+      method_end (w),
+      parent (NULL)
   {
   }
 
@@ -399,6 +404,11 @@
   {
     return method_end;
   }
+
+  /// Return the return type of the method's erasure.  This is simpler
+  /// than going via the erasure of the method, as no enclosing
+  /// context is needed.
+  model_type *get_erased_return_type () const;
 };
 
 /// This represents a method that is the result of merging multiple



More information about the Java-patches mailing list