RE: [sv-ec] question about name resolution in classes

From: francoise martinolle <fm_at_.....>
Date: Thu Mar 30 2006 - 12:02:42 PST
 

I recently thought again about name resolution in classes when the whole
class
hierarchy is not known at parse time. I think it makes sense to allow
it as it is a powerful modeling mechanism. I think that you will encounter
the same
issues with type parameters.
A type parameter could be a class type and any property/method reference
of that class type will not be resolved until elaboration.

parameter type T = myclass;
T v;

initial
   $display (v.i); // i may be a property of the class type, we do not know
what 
                  //  this property is until elaboration

Symbol resolution in the elaborator becomes even more complex when we look
at the effect of package imports.

Here is an interesting example with  import p::*

package p;
  real i = 1.0;
endpackage

module top;

class base_with_i;
  integer i;
endclass

class base;
endclass

  child u1 #(base_with_i);
  child u2 #(base);
endmodule

module child;
parameter type T;

  import p::*;
  class B extends PT;
    $display ( i );
  endclass

endmodule

Suppose we had decided that the reference to i inside class B is not
resolved until elaboration
because the class B does not have a locally static name space (is not
"lexically static", using Gordon's
terminology).
The symbol i is resolved in instance u1 to refer to the property i in class
base_with_i
but in instance u1, the symbol is unresolved... UNLESS the elaborator was
able to 
look at the potential package imports (::*) and resolve i to p::i.

I do not think that the elaborator should be able to look at all import ::*.
The import statement is to make declarations visible for the purpose
of parse time name resolution, they are used to define the visible
declarations in the scope
but they should not be used during elaboration. There are two main reasons:
   1) i not declared in module top, you cannot refer to the package ref i as
"top.i",
   2) the effect of an import declaration is to provide visibility of
foreign symbols for name resolution of 
      references which follow the import, it does not affect
      references which precede (below  example is from  the LRM ).
      The elaborator uses an already established instance hierarchy to
resolve OOMRs (XMRs).
Extracted ex from LRM:
module foo;
import q::*;
wire a = c; // This statement forces the import of q::c;
import p::c; // The conflict with q::c and p::c creates an error.
endmodule

Imagine I have the following:
module top
parameter type PT;
  class B extends PT;
    $display ( i );
  endclass
  import q::i;

  wire w = i;
endmodule

The import q::i makes the symbol i from package q visible for name
resolution of i on the continuous assignment
to wire w but it does not affect the name resolution of i inside class B (
at least during parsing)
since the class name space is not entirely known.
The question is for instance u2, during elaboration should i resolve to q::i
even if it 
is after the class declaration? If the class was locally static, it would
not and i would be undefined.

Imagine I have both imports,  p::* and q::i in module top (see example
below), one before the class and the 
other after the class, we may end up
with both package references used for name resolution if we decide to use
the imports for the
elaboration time name resolution, as a consequence either 'i' cannot be
resolved, or 'i' resolves to q::i
because a select import always takes precedence over an import ::* but that
is at odds with the
behaviour if the class was locally static. In the case where the class
declaration in the example below
is locally static, 'i' resolves to p::i through the import p::* during
parsing.

module top
parameter type PT;
  import p::*;
  class B extends PT;
    $display ( i );
  endclass
  import q::i;

  wire w = i; // this resolves to q::i at parse time.
endmodule

However if I replace import p::* with import p::i, 
module top
parameter type PT;
  import p::i;
  class B extends PT;
    $display ( i );
  endclass
 endmodule
the symbol i from the package p is 
always imported and is visible in the module scope at parse time. The class
does not have a locally static
name space so the resolution of 'i' is postponed until elaboration.
At elaboration , what does i in child instance u2 resolve to? p::i or
undefined?

My opinion is that potential imports and package references which have been
made visible in a scope by
the parser should not be considered when resolving names inside a non
lexically static class or
for any oomr name resolution.
If we were to consider those to resolve OOMRs we would have no choice than
elaborating
all packages which are imported whether or not any of their declarations
would be used because
we do not know until oomr resolution.
Besides the p::name syntax exists and could be used instead of the simple
name to indicate that the symbol 
comes from a package in the case where the class is a non lexically static
class.

This name resolution is somewhat linked to a discussion we had before on
whether or not packages that
are imported are elaborated or not. I do not think that we need to state it
in the standard. Packages
should not be used for name resolution in the elaborator. The import
declarations are an aid to 
make package symbols visible at parse time.

Another issue that we also need to agree on is the order in which the class
hierarchy is searched.
Suppose I put the import p::* inside class B. Is the symbol 'i' lookup in p
before it
is looked up in the base classes of class B? Intuitively, I would think that
the class B scope and the imports
inside class B would be searched before attempting to search the symbol in
the base class.
That would make $display(i) always resolve to p::i:
class B extends PT;
   import p::*;
   $display( i );  // this is p::i
endclass

If you want base_with_i.i, you can use "super.i"

Personnally, I think we should keep name resolution in the elaborator simple
and





-----Original Message-----
From: owner-sv-ec@eda.org [mailto:owner-sv-ec@eda.org] On Behalf Of Gordon
Vreugdenhil
Sent: Saturday, March 25, 2006 11:41 AM
To: francoise martinolle
Cc: sv-ec@eda.org
Subject: Re: [sv-ec] question about name resolution in classes



francoise martinolle wrote:
> Gordon,
> Let me use the term "lexically static" rather than "locally static"
> for this.  Then I think that a resonable approximation would be
> that:
>       A lexically static type is any type *except* the following:
>          1) a type denoted by a type parameter
>          2) a type denoted by a typedef whose type is
>             defined by the "interface_instance_identifier .
type_identifier"
>             rule
>          3) a type defined by a parameterized class specialization
>          4) a typedef of a type that is not lexically static
>          5) a class derived from a type that is not lexically static
> 
> I think that covers all of what I consider to be the "elaboration time 
> types" and is perhaps close to what you had in mind with the "locally 
> static" suggestion.
> 
> 
>>>I like the above definition except for the terminology of lexically 
>>>static type.
> 
>>>It seems that you chose lexical to mean that the type name space is  
>>>entirely known at parse time.

Right.


>>>But I do no have a better idea at the moment.

I couldn't come up with anything better at the time either;  if anyone has
terminology suggestions here, that would be welcome.

It might be more clear to go towards the term "elaboration type"
but that is somewhat misleading as well.


>>>The list you propose seems complete, I have reservations that any 
>>>type  defined by a parameterized class specialization is not 
>>>lexically static. I think it depends on how the parameters are  used 
>>>inside the class.
> 
>>>Just like in your example above the parameter passed as 
>>>specialization
> 
> value of a class may just be used in sizing
> 
>>>a type inside the class:
>>>    class vector #(int size = 1);
>>>        bit [size-1:0] a;
>>>    endclass


Certainly.

Including paramterized classes is primary just me being pretty conservative
about where the language might go.  I don't think there is any reason to
include parameterized class in the list right now, but there are several
directions in which I could see the language going where we might need to
treat them as not lexically static.  For  example, if at some point we'd
want to provide different implementations for particular specializations (as
one can do in C++), it might be difficult to deal with the change if we
require resolution.  The same would be true if one wanted to have some form
of conditional declarations inside a parameterized class ("only override and
define these properties if the type parameter is real").  None of this is
legal right now, but it is certainly possible/likely that people would make
proposals along such fronts.

Having a parameterized class be lexically static under some conditions and
not others might be confusing and be too sensitive to design change.

If we don't include parameterized classes in the list I proposed, we might
be making things more difficult in the future.  I think that I'd rather see
the community gain a bit more experience about complex designs prior to
committing to a direction.
Excluding parameterized classes from being lexically static for now gives us
more space to consider issues and is easy to relax later.

I don't feel too strongly about this so if others are opposed to being
conservative on this, I wouldn't argue too much.

Gord.

--
--------------------------------------------------------------------
Gordon Vreugdenhil                                503-685-0808
Model Technology (Mentor Graphics)                gordonv@model.com
Received on Thu Mar 30 12:03:47 2006

This archive was generated by hypermail 2.1.8 : Thu Mar 30 2006 - 12:04:09 PST