Up to 1800-2017 the grammar in the LRM allowed an optional lifetime
qualifier for class declarations. Icarus supports this and uses this as the
default lifetime for methods of the class. But the LRM never specified what
this qualifier should do actually actually. Starting with 1800-2023 the
qualifier will be removed from the grammar[1].
Furthermore the LRM states that methods of a class are supposed to have
automatic storage and static storage is forbidden.
This currently works in Icarus for the most part since the liftime attached
to class methods is ignored during elaboration in most places. Where it
does not work is for variable initializers where it results in broken code
being generated and vvp crashes at runtime. E.g.
```
class C;
task t;
int x = 10;
endtask
endclass
```
Keep the optional lifetime qualifier for classes in the grammar for now, to
ensure backwards compatibility in case somebody is actually using it. But
ignore it and print a warning if it is specified.
In addition set the default lifetime for all classes to automatic. This
makes sure that variable initialization in classes works as expected.
[1] https://accellera.mantishub.io/view.php?id=3561
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
It is not allowed to create objects of virtual classes. Currently the
virtual keyword is accepted by the parser, but otherwise ignored.
Keep track of whether a class is virtual and generate an error when the
class new operator is used for a virtual type.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Currently when referencing a typedef this gets replaced with the
`data_type_t` that the typedef points to. This works for most cases, but
there are some corner cases where it breaks down.
E.g. it is possible to have a scoped type identifier which references a
type defined in a package. For such type identifiers, only the data_type_t
itself is remembered, but not the package scope. This will cause the type
identifier to be elaborated in the wrong scope.
Furthermore type identifiers of vector types used for module or task port
might not be elaborated in the correct scope.
Introduce a new `typeref_t` which has `data_type_t` as a base type and can
be used as the data type for a signal. A new instance of a `typeref_t` is
created when referencing a type identifier. The `typeref_t` remembers both
the data type and the scope of the type identifier.
When elaborating the `typeref_t` the elaboration is passed through to the
referenced `data_type_t`. But special care is taken to lookup the right
scope first.
With the new approach also typedefs of typedefs are supported. This
previously did not work because chained typedefs all reference the same
`data_type_t`, but each typedef sets the `name` field of the `data_type_t`.
So the second typedef overwrites the first typedef and a lookup of the
scope of the first typedef by name will fail as it will return the scope of
the second typedef.
This refactoring also allows to define clear ownership of a data_type_t
instance. This e.g. means that an array type owns its base type and the
base type can be freed when the array type itself is freed. The same is
true for signals and class properties, they now own their data type and the
data type can be freed when the signal or property is freed.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This will allow to generate error messages that point to the right line if
there is something wrong or not supported in a class property declaration.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows unpacked array dimensions on non-ANSI style task and
function ports.
To support this refactor pform_make_task_ports() to accept a of
pform_port_t, which in addition to the identifier name also allows to
specify per port unpacked dimensions.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
SystemVerilog allows `parameter` and `localparam` to declare constants
within a class scope. E.g.
```SystemVerilog
class C;
localparam A = 10;
endclass
```
In this context both declare a local parameter that can not be overwritten.
Supporting this can be achieved for the most part by adding a parser
sub-rule in class declaration rule. In addition some extra support code is
needed to mark the parameter as non-overridable.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
When creating an enum type it must be added to the scope where it is
declared so it can later be elaborated and the enum and its names can be
referenced in expressions.
In addition the names of the enum must be added to the lexor scope so that
name collisions are detected and can be reported as errors.
This is done with pform_put_enum_type_in_scope() function.
At the moment the function is called from two different places
* When adding a typedef of a enum type
* When creating a signal of a enum type
In addition the enum_type_t is added to a class scope `enum_sets` when
declaring a enum property in a class. But this only makes sure that the
enum gets elaborated, its names are not added to the lexor scope.
This works fine for the most part, but breaks for a few corner cases.
E.g. it is possible to declare a enum type as part of the subtype of
another packed type such as structs or packed arrays. E.g.
```
struct packed {
enum {
A
} e;
} s;
```
This is not covered by either of the cases above and neither do the names
of the enum get added to the lexor scope, nor is the enum type elaborated.
Another corner case that is currently not working is declaring a class
property where the type is a typedef of a enum that is declared outside of
the class. In this case the enum is elaborated again inside the class
scope. E.g. the below is supposed to work, but fails with an already
declared symbol error.
```
typedef enum {
A
} e_t;
class C;
typedef enum {
A
} e1;
e_t e2;
endclass
```
In addition since for enums declared in classes they are only added to
`enum_sets`, but names are not added to the lexor scope, it is possible to
declare a different symbol in the class scope with the same name.
E.g. the following elaborates fine
```
class C;
enum {
A
} e;
typedef int A;
endclass
```
To fix this call pform_put_enum_type_in_scope() when the enum_type_t is
created in the parser. This makes sure that it is handled the same
regardless where the type is declared or used.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This.new is not allowed.
super.new beyond the first statement is not allowed.
And while I'm at it, clean up the use of "@" and "#" in
the code as tokens for this and super.
Static properties are like variables in a named scope.
Detect these variables during elaboration so that the
code generator just sees them as variables.
Class methods belong in a class scope, not the containing module.
So create a lexical scope that carries tasks and functions and
create a PClass to represent classes.