[sv-bc] Re: [sv-ec] Name resolution issues

From: Gordon Vreugdenhil <gordonv_at_.....>
Date: Mon Aug 06 2007 - 09:50:11 PDT
I read over Mark's suggestions regarding name resolution.  My
initial reactions are below.




() Lack of Rules

Mark's write-up has some interesting examples and
suggestions about deferred handling of name resolution
but there are no clear rules about how to apply
the suggested approaches.  The lack of definite rules
makes it difficult to extrapolate the examples to non-trivial
circumstances.


() Bind semantics

I completely disagree with Mark's assumption that "bind"
may cause more names to be imported.  Adding names to a
scope during elaboration is a recipe for error prone design.
Mark's example is a trivial case that appears to be desirable,
but he has not provided addressed non-trivial cases involving,
for example, binding into a nested module.  In such cases,
the outer lexical scope is in play as well as the inner
nested module scope.  Allowing the bind to impact the effective
import name set in the outer scope coule invalidate code in
that scope.  A instance introduced by a "bind" is supposed
to use the names that exist in the scope of the target -- I don't
agree with Mark that bind should be allowed to introduce names
(even potential names) from a lexically visible import.


() Class properties

Although I don't necessarily object to allowing forward
references to class property names, the rules for this
have to be specified very carefully.  C++, for example,
does not allow a name use to change kind from
a type to non-type or vice versa if the name is visible
via a "use" prior to its existence as a class property.
This would be very important to handle in any proposed set
of rules.  In addition, there may well be interactions
with the move macro-like handling of assertions constructs
within classes.


() Inline constraints

I have substantial problems with the direction that Mark
is suggesting for inline constraints.  In particular,
the deferred error handling is going to be highly susceptible
to changes in user code and won't be detectable until
elaboration time.  In complex design scenarios such very
late detection of basic name conflicts are going to cause
serious usability problems.

In addition, there is still no way, given Mark's suggestion,
to reliable write a function such as:
    function void do_rand(class_type c, int x);
        c.randomize with {prop < x};
    endfunction

Since "x" can bind into "c" due to remote and incidental
changes to "class_type" AND since you can't name the
function formal in an authoritative manner, such cases
will make writing robust code very difficult.



() Hierarchical name rules

Mark has not discussed how dotted names (hierarchical or
selects) are to be handled.  This is a critical aspect
of any proposal.  How and when one commits to a "select"
versus a hierarchical name for a dotted name is a
fundamental question.






Summary

I am pretty seriously opposed to Mark's direction of
making name resolution substantially more dynamic.  Such
approaches lead to less reliable systems and poor composition
of design components.  Although technically feasible,
I seriously question the long term impact this is
going to have on design composition for the users.

A design component should have a clearly defined set of
interaction rules (I want to say "interface" but don't
want to confuse that with the SV interface!).

I think that it would be much better to work on mechanisms
for having specifications in the more dynamic circumstances
that state the invariants that must hold for the design
to be valid.  A design component can then be separately
compiled with knowledge that its internal behavior cannot
be compromised if the specification holds.

Most of the issues involve class interactions with type
parameters.  Why not just provide a specification for
the type parameter such that one has definite knowledge
early?  Something along the lines of a Java interface is
what I have in mind.  This could easily be expressed in
the current language by changing the semantics of a
type parameter default to mean "any override must satisfy
the name resolution of the default".  So a class default
provides essentially a specification of the names that
any override must provide.

Such a change would be simple to make and would address
the more dynamic cases without weakening system composition
in the manner that Mark is suggesting.

Gord




Mark Hartoog wrote:
> Problem statement:
> First I am going to describe the name resolution problems I am
> attempting to address. The name resolution issues arise because of
> several features:
> 
> 1) Wild card package imports.
> 2) Segmented $unit
> 3) Late binding caused by type parameters.
> 4) Late binding of identifiers in bind statements.
> 5) Forward references to fields in classes.
> 
> In Verilog-2005 all non-dotted identifiers, except for task and function
> calls, can be resolved as they are parsed. For simplicity I am going to
> leave task and function identifiers out of the discussion for the
> moment. 
> 
> The basic question is should we continue to require all of these
> non-dotted identifiers to be resolved at parse time, or will some of the
> identifiers be resolved later. There are several later points where they
> could be resolved. These include end of scope, end of module, end of
> compilation unit, and at elaboration. 
> 
> In the case of bind statements, we have little choice. Non-dotted
> identifiers in bind statements have to be resolved at elaboration time.
> Section 22.10 says that the effect of a bind to is to create an instance
> at the end of the target scope. Identifiers in the bind statement can
> clearly bind to any symbol in that scope. Although the LRM does not say,
> they presumably can also bind to symbols that have been imported into
> that scope. What is less clear is whether they can bind to symbols in
> $unit defined after the target module (same for nested modules) or
> whether the bind statement can cause additional symbols to be wild card
> imported into the scope. 
> 
> In addition to bind the language features or potential features that
> cause problems are:
> 
> 1) Randomize with blocks:
> 
> module m #(type TP = CB) ();
>      int a;
>      TP x;
>       initial x.randomize with { a < 1 };
> 
> The identifier'a' is suppose to be resolved first in the scope of the
> variable 'x,' but the type of 'x' is a type parameters, so the scope
> cannot be examined until elaboration. The only way to fix this so it can
> be resolved at parse time is to make a backwards incompatible change to
> the syntax to force users to indicate which identifiers must resolved in
> the scope of the variable.
> 
> 2) Forward references to fields in classes:
> 
> int a;
> class C;
>     function int f();
>         return a;
>     endfunction
>     int a;
> endclass
> 
> Both C++ and Java require declaration before use for variables in
> procedural contexts like Verilog, but they allow use before declaration
> of class fields and methods. I think users expect this behavior in
> classes. This can be implemented by delaying the resolution of
> identifiers in classes until end of class. 
> 
> 3) Forward typedefs as base class:
> 
> typedef class CB;
> int a;
> class C extends CB;
>     function int f();
>         return a;
>     endfunction
> endclass
> class CB;
>     int a;
> endclass
> 
> This feature is in P1800-2005, so removing it would be a backwards
> compatibility issue. On the other hand it really doesn't add anything
> useful to the language, so it probably could be removed without causing
> any serious problems. 
> 
> To implement this you have to delay resolution of identifiers in classes
> to end of compilation unit, since worse case the base class could be a
> forward typedef in $unit. 
> 
> 4) Type parameters as base class.
> 
> module m #(type TP = CB) ();
>      int a;
>      class C extends CB;
>          function int f();
>              return a;
>         endfunction
>     endclass
> 
> It is unclear if this is allowed in P1800-2005. I know of several
> implementation that have taken a liberal interpretation of
> class_identifier to include type parameters that are assigned class
> types in other contexts where the LRM indicates a class_identifer is
> required. I have also gotten customer inquires as to why they cannot use
> a type parameter for a base class. I do not know of any implementations
> that actually allows this. In C++ and Java template classes this is
> illegal.
> 
> If this is allowed in the language, then identifiers in class task and
> functions cannot be resolved until elaboration. (Note: I am not
> proposing that we add this now. I included this here, because customers
> have asked why they cannot do this, and this would be completely ruled
> out by requiring early name resolution.)
> 
> Proposals to address these Problems
> 
> Making the language so that all simple identifiers can be resolved at
> parse time will simplify tools. On the other hand, if some identifiers
> are not going to be resolved till elaboration, then I don't see why we
> should be making incompatible changes in the language or making the
> language less powerful to reduce the number of cases that identifiers
> are resolved after parse time. 
> 
> The important question is can we resolve the identifiers later, say at
> end of compilation unit or elaboration, and obtain results that are
> consistent with the wild card package import rules and $unit scoping
> rules. I believe this is possible. Here is an example I will use to
> illustrate the algorithm:
> 
> package P;
>      int a;
> endpackage
> 
> class SomeClass;
> endclass
> 
> module m #(type TP = SomeClass);
>    import P::*;
>    TP x;
>    initial x.randomize with { a < b };
>    int a;  // should be an error if 'a' was wild card imported by
> randomize.
> endmodule 
> 
> int b; // 'b' in randomize should never bind to this, because it is
> after module
> 
> class Ok;
>     int a;
>     int b;
> endclass
> 
> class Bad;
>    int c, d;
> endclass
> 
> module top;
>    m #(Ok) m1();
>    m #(Bad) m2();
> endmodule
> 
> In instance top.m1, everything is fine. The identifiers 'a' and 'b'
> resolve to fields of the class Ok. In instance top.m2, neither 'a' nor
> 'b' can be resolved in the class Bad. Here 'a' should cause the wild
> card package import of P::a. This would then make the declaration of 'a'
> later in the module an error. The identifier 'b' should have no
> resolution and be an error. It should not resolve to $unit::b because
> that declaration is after the module.
> 
> In this case these identifiers can only be processed at elaboration
> time, because only then is the scope of the variable 'x' known. The
> technique described below can resolve these correctly and produce the
> correct error messages. This may not be the only way of doing this. It
> is just one way of doing it.
> 
> To do this we need to identify a candidate resolution for each
> identifier in the randomize with block at parse time. We record this
> resolution as a candidate, but do not actually make the resolution. In
> the example above, the candidate for 'a' would be the wild card package
> import of P::a. We identify P::a as the candidate, but we do not
> actually make the package import. When we parse the declaration of 'a'
> later, this declaration is legal because P::a has not been imported. For
> 'b' there is no candidate resolution at parse time. We record a null
> candidate resolution.
> 
> At elaboration time we try to resolve 'a' and 'b' in the scope of the
> variable 'x'. In top.m1, they both resolve, and everything is fine. In
> top.m2, neither 'a' nor 'b' resolves in the scope of 'x'. The only other
> resolution that can be considered for 'a' or 'b' at elaboration time is
> the candidate that was identified at parse time. In top.m2, there is a
> candidate for 'a'. To resolve 'a' to this candidate we would have to
> wild card import P::a, but this wild card import is no longer legal.
> There is now a symbol 'a' in the scope of module 'm', so instead of
> resolving 'a', we give an error message that the declaration of 'a' is
> illegal because 'a' has been wild card imported. For 'b' there was no
> parse time candidate, so we simply give an unresolved identifier error
> message. 
> 
> A similar thing can be done in classes, even in classes where the base
> class is a type parameter.
> 
> This does not address the issues with bind. Bind statements are not in
> the target context at parse time, so you cannot have parse time
> candidates for them. The basic issue is illustrated by this example:
> 
> package P;
>    int a;
> endpackage
> 
> module m;
>    import P::*;
> endmodule
> 
> module b (input int x, y);
> endmodule
> 
> int b;
> 
> module top;
>     m m1();
>     bind m: m1 b b1(a, b);
> endmodule
> 
> Here 'a' and 'b' need to be resolved at elaboration time in module 'm'.
> If this instantiation were present at the end of the module 'm' at parse
> time, 'a' would resolve to P::a and cause 'a' to be wild card imported
> into 'm'. I think it should still do this at elaboration time. The
> identifier 'b' is a little trickier. We cannot use parse time candidates
> here. The choices seem to be
> 
> 1) Let 'b' resolve to $unit::b.
> 2) Do not allow any bind identifiers to resolve in $unit. If you want it
> to resolve to $unit, the user can put $unit::b in the bind statement.
> (Note: this is a little strange. It is the $unit of the target, not the
> $unit that contains the bind statement)
> 3) Require tools to have a segmented view of $unit so that identifiers
> can be correctly resolved.
> 
> I think either 1 or 2 are good enough, but I can live with 3 too.
> 
> Summary
> 
> It is possible to resolve all simple identifiers at parse time as others
> have proposed. To do this we need to:
> 
> 1) Make backwards incompatible changes in the language.
> 2) Forever rule out useful features like forward references to class
> members, a feature that most other languages with classes allow. 
> 
> I see no reason to do this. The Verilog language has always required
> name resolution at elaboration time for task, function and hierarchical
> names. The bind construct clearly requires name resolution at
> elaboration for simple identifiers. I see no compelling reason for
> making backwards incompatible changes to avoid a few more cases of
> delayed resolution of simple identifiers.
> 
> 
> 

-- 
--------------------------------------------------------------------
Gordon Vreugdenhil                                503-685-0808
Model Technology (Mentor Graphics)                gordonv@model.com


-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Mon Aug 6 09:50:54 2007

This archive was generated by hypermail 2.1.8 : Mon Aug 06 2007 - 09:51:04 PDT