core: allow calling parent methods #497
No reviewers
Labels
No Milestone
No Assignees
3 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: M-Labs/nac3#497
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue-133"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR adds support for calling other class or ancestor functions as discussed in #133. It also adds capability to call parent functions #136 (the
super
keyword is not declared but the functionality is achieved by specifying class names).Initialization of variables can be done as:
However, unlike Python when overriding, the function signature must be same. Moreover calling class functions with
self
is already mapped to the corresponding class function definition IDs, this means that:will output 1 in nac3 and 2 in Python. The Python behavior can be supported by evaluating the definitionIDs of the function on each call, but again this is because of Python's lax type system and in other languages like C++
this
behaves similar toself
in nac3. I will create another PR to support the virtual polymorphism if its needed.The tests have been added to
nac3standalone/demo/src/inheritance.py
Fixes #133.
@ -791,1 +814,3 @@
result.extend(Self::get_all_assigned_field(body.as_slice())?);
result.extend(Self::get_all_assigned_field(class_name, ast, body.as_slice())?);
}
// Variables Initiated in function calls
Initiated ?
Should have been initialized. This part checks if there are any function calls in the
__init__
block where user might have initialized some variables (as all variables must be initialized before creating an object).@ -1823,3 +1823,3 @@
unreachable!("must be init function here")
}
let all_inited = Self::get_all_assigned_field(body.as_slice())?;
// Since AST stores class names without prepending `__module__.`, we split the name for search purposes
I don't understand this comment, can you elaborate?
Is a string hack the best way of solving this?
Class names in TopLevelDef are stored by first prepending module name to the class name. So,
would be named __main__.A. However, in the raw AST that holds the statements, the name is just A, so here I am removing the module name from definition.
Hmm this was the most straightforward method. An alternative could be to have to do the search in the TopLevelDef and get the corresponding DefinitionID to fetch the function statements from the AST.
Seems to me that doing the alternative method would make it clear what is happening from the code alone, and would not need any of those obscure/complicated comments.
Please add "Fixes #133" to the end of the PR message to make it explicitly clear this PR closes that issue.
Have you tried this with an ARTIQ kernel?
@ -1826,0 +1828,4 @@
class_name_only.split_once('.').unwrap_or(("", &class_name_only));
let all_inited = Self::get_all_assigned_field(
class_name_only.into(),
Would this way of getting the fully qualified name of the to-be-initialized class work for inner (nested) classes?
Hmm I tried testing on inner classes, but they are not currently supported in nac3. Also, by matching name against the class names in the TopLevelDef as suggested in #497 (comment) we can avoid the problem altogether.
@ -734,3 +735,3 @@
}
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, HashSet<String>> {
pub fn get_all_assigned_field(
Could you add documentation to this function?
Will do.
@ -792,0 +824,4 @@
let ExprKind::Name { id, .. } = &value.node else {
continue;
};
// Need to conside the two cases:
NIt: consider
@ -1675,0 +1678,4 @@
/// Returns [`None`] if its not a call to parent method, otherwise
/// returns a new `func` with class name replaced by `self` and method resolved to its `DefinitionID`
///
/// e.g. A.f1(self, ...) returns Some(self.DefintionID(f1))
Do you mean it returns
Some(DefinitionID(self.f1))
? Aren'tDefinitionID
s global?Since each class function is assigned a unique DefintionID, this returns replaces the attribute with the definitionID of the actual function to be called. To illustrate:
Here
Foo.f1()
andBar.f1()
will be assigned different Ids say 1 and 2. The call toFoo.f1(self)
will be changed byself.1()
. Under normal execution when generating function call ingen_expr
, a lookup is done on the class definition to find the definitionId of the corresponding method. Here, I am doing the lookup beforehand to point to the correct method.Could you wrap
DefinitionID
with some symbol (e.g.self.{DefinitionID(f1)}
) to indicate that the returnedDefinitionID
is supposed to be substituted by the function returns?adadbaa6ba
to79e95653cd
79e95653cd
to415c78d23b
After testing with ARTIQ kernels, the polymorphism is working when functions of both classes are defined to be on same side (i.e. either both are
@kernel
or@rpc
). So, this is currently breaking:The reason for this is that during the
TopLevelDef
during type checking for the@kernel
methods does not have definitions of other non kernel functions and so is unable to findoutput_all_fields
in classA
.What is the error message emitted here?
The error is from
The error is raised since the function (
A.output_all_fields
) was not marked with one of the decorators (rpc
,kernel
,portable
), its definition is ignored and is not part of the definition list atportable
orrpc
.I am not too sure if we want to support this way of calling functions though. Since we are enforcing signatures to be same for overridden functions, wouldn't it be better to add check to ensure they are declared with same decorators as well.
Please check that this also works with ARTIQ.
I have tested the changes on ARTIQ (nac3 branch) and the polymorphism is working as expected there as well. The above code will output:
No further comments.