In my ray-tracer (Glome), I treat instances as just a container for some other object along with a transformation matrix, that itself behaves like a regular object. Every kind of object has a method to construct a bounding box, so that when building an acceleration structure that contains instances, if I remember correctly, I take the 8 corners of the bounding box of the contained object, transform them, and constructs a new bounding box around the transformed points. It's not optimal, but it works okay.
Another way of working around your problem is to just pass the current instance (or a list of instances) as an argument to your ray intersection function. (I use a similar trick for textures, and also for a tagging scheme where I can apply arbitrary tags to objects and then pass back a list of all the tags of objects that the ray hit when I return a ray intersection.)