Skip to content

Commit

Permalink
Added implicit inverse relation assignment for bidirectional relation…
Browse files Browse the repository at this point in the history
…s with an index (PR #4989)

Pull request opened by the merge tool on behalf of #4989
  • Loading branch information
sanderr authored and inmantaci committed Oct 19, 2022
1 parent 639c8b2 commit 2ccf220
Show file tree
Hide file tree
Showing 12 changed files with 555 additions and 57 deletions.
9 changes: 9 additions & 0 deletions changelogs/unreleased/4982-implicit-relation-inverse.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
description: Added implicit inverse relation assignment for bidirectional relations with an index
change-type: minor
sections:
feature:
Constructors that appear as a right hand side in an assignment (or another constructor) now no longer require
explicit assignments for the inverse relation to the left hand side.
destination-branches:
- iso5
- master
2 changes: 1 addition & 1 deletion src/inmanta/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
Contact: code@inmanta.com
"""

COMPILER_VERSION = "2022.4"
COMPILER_VERSION = "2022.5"
RUNNING_TESTS = False
"""
This is enabled/disabled by the test suite when tests are run.
Expand Down
3 changes: 2 additions & 1 deletion src/inmanta/ast/constraint/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from inmanta import stable_api
from inmanta.ast import LocatableString, RuntimeException, TypingException
from inmanta.ast.statements import (
AttributeAssignmentLHS,
ExpressionStatement,
Literal,
ReferenceStatement,
Expand Down Expand Up @@ -236,7 +237,7 @@ class LazyBooleanOperator(BinaryOperator, Resumer):
def __init__(self, name: str, op1: ExpressionStatement, op2: ExpressionStatement) -> None:
Operator.__init__(self, name, [op1, op2])

def normalize(self) -> None:
def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
super().normalize()
# lazy execution: we don't immediately emit the second operator so we need to hold its promises until we do
self._own_eager_promises = list(self.children[1].get_all_eager_promises())
Expand Down
4 changes: 2 additions & 2 deletions src/inmanta/ast/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def set_attributes(self, attributes: "Dict[str,Attribute]") -> None:

def is_parent(self, entity: "Entity") -> bool:
"""
Check if the given entity is a parent of this entity
Check if the given entity is a parent of this entity. Does not consider an entity its own parent.
"""
if entity in self.parent_entities:
return True
Expand Down Expand Up @@ -337,7 +337,7 @@ def get_instance(

def is_subclass(self, cls: "Entity") -> bool:
"""
Is the given class a subclass of this class
Is the given class a subclass of this class. Does not consider entities a subclass of themselves.
"""
return cls.is_parent(self)

Expand Down
19 changes: 17 additions & 2 deletions src/inmanta/ast/statements/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,24 @@ def _fulfill_promises(self, requires: Dict[object, object]) -> None:
promise.fulfill()


@dataclass(frozen=True)
class AttributeAssignmentLHS:
instance: "Reference"
attribute: str


class ExpressionStatement(RequiresEmitStatement):
__slots__ = ()

def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
"""
:param lhs_attribute: The left hand side attribute if this expression is a right hand side in an attribute assignment.
If not None, that caller is responsible for making sure the reference resolves to the correct instance as soon as
this statement enters the `requires_emit` stage. As a result, it should always be None if the instance construction
depends on this statement.
"""
raise NotImplementedError()

def as_constant(self) -> object:
"""
Returns this expression as a constant value, if possible. Otherwise, raise a RuntimeException.
Expand Down Expand Up @@ -462,7 +477,7 @@ def __init__(self, children: List[ExpressionStatement]) -> None:
self.children: Sequence[ExpressionStatement] = children
self.anchors.extend((anchor for e in self.children for anchor in e.get_anchors()))

def normalize(self) -> None:
def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
for c in self.children:
c.normalize()

Expand Down Expand Up @@ -523,7 +538,7 @@ def __init__(self, value: object) -> None:
self.value = value
self.lexpos: Optional[int] = None

def normalize(self) -> None:
def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
pass

def __repr__(self) -> str:
Expand Down
14 changes: 12 additions & 2 deletions src/inmanta/ast/statements/assign.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from inmanta.ast.attribute import RelationAttribute
from inmanta.ast.statements import (
AssignStatement,
AttributeAssignmentLHS,
ExpressionStatement,
RequiresEmitStatement,
Resumer,
Expand Down Expand Up @@ -86,6 +87,11 @@ def __init__(self, items: typing.List[ExpressionStatement]) -> None:
ReferenceStatement.__init__(self, items)
self.items = items

def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
for item in self.items:
# pass on lhs_attribute to children
item.normalize(lhs_attribute=lhs_attribute)

def requires_emit_gradual(
self, resolver: Resolver, queue: QueueScheduler, resultcollector: Optional[ResultCollector]
) -> typing.Dict[object, VariableABC]:
Expand Down Expand Up @@ -220,6 +226,10 @@ def __init__(self, instance: "Reference", attribute_name: str, value: Expression
self.list_only = list_only
self._assignment_promise: StaticEagerPromise = StaticEagerPromise(self.instance, self.attribute_name, self)

def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
# register this assignment as left hand side to the value on the right hand side
self.rhs.normalize(lhs_attribute=AttributeAssignmentLHS(self.instance, self.attribute_name))

def get_all_eager_promises(self) -> Iterator["StaticEagerPromise"]:
# propagate this attribute assignment's promise to parent blocks
return chain(super().get_all_eager_promises(), [self._assignment_promise])
Expand Down Expand Up @@ -433,7 +443,7 @@ def __init__(
self.query = [(str(n), e) for n, e in query]
self.wrapped_query: typing.List["WrappedKwargs"] = wrapped_query

def normalize(self) -> None:
def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
ReferenceStatement.normalize(self)
self.type = self.namespace.get_type(self.index_type)

Expand Down Expand Up @@ -497,7 +507,7 @@ def __init__(
self.querypart: typing.List[typing.Tuple[str, ExpressionStatement]] = [(str(n), e) for n, e in query]
self.wrapped_querypart: typing.List["WrappedKwargs"] = wrapped_query

def normalize(self) -> None:
def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
ReferenceStatement.normalize(self)
# currently there is no way to get the type of an expression prior to evaluation
self.type = None
Expand Down
4 changes: 2 additions & 2 deletions src/inmanta/ast/statements/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
TypeReferenceAnchor,
WrappingRuntimeException,
)
from inmanta.ast.statements import ExpressionStatement, ReferenceStatement
from inmanta.ast.statements import AttributeAssignmentLHS, ExpressionStatement, ReferenceStatement
from inmanta.ast.statements.generator import WrappedKwargs
from inmanta.execute.dataflow import DataflowGraph
from inmanta.execute.proxy import UnknownException, UnsetException
Expand Down Expand Up @@ -81,7 +81,7 @@ def __init__(
self.kwargs[arg_name] = expr
self.function: Optional[Function] = None

def normalize(self) -> None:
def normalize(self, *, lhs_attribute: Optional[AttributeAssignmentLHS] = None) -> None:
ReferenceStatement.normalize(self)
func = self.namespace.get_type(self.name)
if isinstance(func, InmantaType.Primitive):
Expand Down
Loading

0 comments on commit 2ccf220

Please sign in to comment.