Re: Modelling Disjoint Subtypes

From: Marshall <marshall.spight_at_gmail.com>
Date: 26 Mar 2007 12:32:15 -0700
Message-ID: <1174937535.353341.203170_at_p77g2000hsh.googlegroups.com>


On Mar 26, 7:18 am, Bob Badour <bbad..._at_pei.sympatico.ca> wrote:

>

> But even if the type checking were deferred until runtime in the
> conditional test, the type of x and y is statically known in the block
> under consideration.

THAT was the point I was trying to make.

Regardless of how we got there, inside the block, the compiler knows statically that the x and y values are integers, even though their declared type in the code is rational. There can exist implementations of rationals such that when the rationals are integers, the generated code for a multiply is identical to the generated code for multiplying integers. So within the context of that block, looking at the generated code, you would not be able to distinguish those two cases. The types have been erased.

Yes, you can infer some amount of information from the generated code. But you definitely can't infer everything; the transformation from source to assembly is a lossy transformation.

Consider the following C code:

struct circle {
  float x;
  float y;
  float radius;
};

struct elevation {
  int x;
  int y;
  float height;
};

void scaleCircle(struct circle *c, float scaleFactor) {   c->radius *= scaleFactor;
}

void scaleElevation(struct elevation *e, float scaleFactor) {   e->height *= scaleFactor;
}

The first function works on 2D circles. Maybe it's part of a drawing program, and the function can grow or shrink circles. The second function works on geographic data; you have a big 2D grid of points, and at various integral spots on the grid you have some elevation data, so this is a 3D program. Some 3D geographic rendering programs support exaggeration of elevation values; it makes some terrain features easier to think about.

Looking at the assembly for each of the above functions, you'll see that the code is taking an offset of 8 bytes from argument one and multiplying it by the second argument. What do we know about the struct that's passed in as the first argument? What's in those 8 bytes? Inside the compiler at compile time, we know exactly what they are: two floats in the first case and two ints in the second. Looking at the compiled code, we have no idea whatsoever about what's in that 8 bytes, because that information has been erased. In fact, those two functions generate *identical code.* Going still further, I could imagine a smart compiler noticing this and putting the generate code in only once. Calls to scaleCircle() and scaleElevation() would go to the same place.

Marshall

PS. I did empirically verify that gcc produces identical sequences of instructions for the two functions. Received on Mon Mar 26 2007 - 21:32:15 CEST

Original text of this message