Repository: pymoca/pymoca

Issue:
Error in 0.10.0 when setting alias type
Pymoca 0.10.0 transfer_model seems to crash when setting an alias type.

Minimal example: a package file
```Modelica
package Package
  type Length = Real;
  type Height = Length;
end Package;
```
with submodel
```Modelica
model SubModel
  input Package.Height H;
end SubModel;
```
and model
```Modelica
model Model
  SubModel var(H=5);
equation
  // var.H = 5;
end Model;
```
and python file
```python
from pathlib import Path
import pymoca.backends.casadi.api

model_folder = Path(__file__).resolve().parent

model = pymoca.backends.casadi.api.transfer_model(model_folder, "Model")
```
all in the same folder. When running the python file with python 3.9, pymoca 0.10.0, casadi 3.6.3, it gives the following error message:

> pymoca.tree.ModificationTargetNotFound: Trying to modify symbol value, which does not exist in class Package.Height

It works fine when replacing `Height` with `Length` in the submodel file. It also works fine when replacing `var(H=5)` with `var` and uncommenting the equation in the model file.


Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pymoca/pymoca/pull/305?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) Report
All modified lines are covered by tests :white_check_mark:
> Comparison is base [(`fd9b6ff`)](https://app.codecov.io/gh/pymoca/pymoca/commit/fd9b6ffd1de039faf7efccf7e17e36f266b30291?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) 80.90% compared to head [(`b54ccca`)](https://app.codecov.io/gh/pymoca/pymoca/pull/305?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) 80.90%.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #305   +/-   ##
=======================================
  Coverage   80.90%   80.90%           
=======================================
  Files          17       17           
  Lines        4131     4131           
  Branches      931      931           
=======================================
  Hits         3342     3342           
  Misses        644      644           
  Partials      145      145           
```


| [Files](https://app.codecov.io/gh/pymoca/pymoca/pull/305?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca) | Coverage Δ | |
|---|---|---|
| [src/pymoca/tree.py](https://app.codecov.io/gh/pymoca/pymoca/pull/305?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca#diff-c3JjL3B5bW9jYS90cmVlLnB5) | `92.75% <100.00%> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pymoca/pymoca/pull/305?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pymoca).

jackvreeken: Has definitely been a while since I looked at the flattening/lookup code, but I think I understand the changes (I stepped through them with the debugger). So LGTM, feel free to merge whenever :)

One question I have for you related to the future fully spec-compliant instantiation, are you getting rid of the special variable name "__value"? It's probably an ugly workaround I came up with to make things work, but if someone out there actually defines their own variable with that name I don't know what will happen 😅
kentrutan: Thanks for checking it out.

I expect that I'll be able to get rid of the special `__value` *and* `__builtin` code, but I'm working on a first step of making the instantiation and flattening steps line up better with the spec by dicing up the current code with a minimum of changes. Next steps are cleaning it up. I'm in the middle of fleshing it out, so the plan is fluid. I'll try to get to a point where something is working (or at least half-baked and understandable :-) and put out a draft PR for some feedback. If I can only find time to work on it 😉.

Patch:
diff --git a/src/pymoca/tree.py b/src/pymoca/tree.py
index 3276c6f..0d94a9c 100644
--- a/src/pymoca/tree.py
+++ b/src/pymoca/tree.py
@@ -427,9 +427,12 @@ def build_instance_tree(
 
     # Check that all symbol modifications to be applied on this class exist
     for arg in extended_orig_class.modification_environment.arguments:
-        if arg.value.component.name not in extended_orig_class.symbols:
+        if (
+            arg.value.component.name not in extended_orig_class.symbols
+            and arg.value.component.name not in ast.Symbol.ATTRIBUTES
+        ):
             raise ModificationTargetNotFound(
-                "Trying to modify symbol {}, which does not exist in class {}".format(
+                'Trying to modify symbol "{}", which does not exist in class {}'.format(
                     arg.value.component.name, extended_orig_class.full_reference()
                 )
             )
@@ -450,6 +453,8 @@ def build_instance_tree(
                 for x in extended_orig_class.modification_environment.arguments
                 if isinstance(x.value, ast.ElementModification)
                 and x.value.component.name == sym_name
+                or sym_name == "__value"
+                and x.value.component.name == "value"
             ]
 
             # Remove from current class's modification environment
@@ -551,7 +556,7 @@ def build_instance_tree(
                 sym.type = build_instance_tree(c, sym.class_modification, c.parent)
             except Exception as e:
                 error_sym = str(orig_class.full_reference()) + "." + sym_name
-                raise type(e)('Processing failed for symbol "{}"'.format(error_sym)) from e
+                raise type(e)('Processing failed for symbol "{}":\n{}'.format(error_sym, e)) from e
 
             sym.class_modification = None
 


Test patch:
diff --git a/test/parse_test.py b/test/parse_test.py
index 24a24e6..e614abf 100644
--- a/test/parse_test.py
+++ b/test/parse_test.py
@@ -678,6 +678,41 @@ class ParseTest(unittest.TestCase):
         self.assertEqual(aircraft.symbols["accel.ma_x"].comment, "measured acceleration")
         self.assertEqual(aircraft.symbols["body.g"].comment, "")
 
+    def test_derived_type_value_modification(self):
+        """Test modifying the value of a derived type"""
+        txt = """
+            package A
+                type X = Integer;
+                type Y = X; /* Derived type */
+                model B
+                    Y y = 1; /* Modification 1 */
+                end B;
+                model C
+                    B c(y = 2); /* Modification 2 */
+                end C;
+                model D = C(c.y = 3); /* Modification 3 */
+                model E
+                    D d(c.y = 4); /* Modification 4 */
+                end E;
+            end A;
+        """
+        ast_tree = parser.parse(txt)
+        class_name = "A.D"
+        comp_ref = ast.ComponentRef.from_string(class_name)
+        flat_tree = tree.flatten(ast_tree, comp_ref)
+        self.assertIsNone(flat_tree.classes[class_name].symbols["c.y"].value.value)
+        self.assertEqual(flat_tree.classes[class_name].equations[0].left.name, "c.y")
+        self.assertEqual(flat_tree.classes[class_name].equations[0].right.value, 3)
+
+        # Parsing AST again, otherwise there is an error in A.E flattening
+        ast_tree = parser.parse(txt)
+        class_name = "A.E"
+        comp_ref = ast.ComponentRef.from_string(class_name)
+        flat_tree = tree.flatten(ast_tree, comp_ref)
+        self.assertIsNone(flat_tree.classes[class_name].symbols["d.c.y"].value.value)
+        self.assertEqual(flat_tree.classes[class_name].equations[0].left.name, "d.c.y")
+        self.assertEqual(flat_tree.classes[class_name].equations[0].right.value, 4)
+
 
 if __name__ == "__main__":
     unittest.main()