Example #1
0
    def create_internal_property(
            self, name: str, expr: Optional[AbstractExpression],
            type: Optional[CompiledType]) -> Optional[PropertyDef]:
        """
        Create an internal property for this env spec.

        If ``expr`` is None, do not create a property and return None.
        Otherwise, unsugar it.

        :param name: Lower-case name to use to create this property name.
            Since the property is internal, the name is decorated.
        """
        assert self.ast_node is not None

        if expr is None:
            return None

        expr = unsugar(expr)
        p = PropertyDef(expr,
                        AbstractNodeData.PREFIX_INTERNAL,
                        name=names.Name('{}_{}'.format(
                            name, next(self.PROPERTY_COUNT))),
                        public=False,
                        type=type,
                        ignore_warn_on_node=True)
        p._indexing_name = '_{}'.format(p.original_name.lower)
        p._original_name = names.Name.from_lower(p._indexing_name)
        p.location = getattr(expr, 'location') or self.location
        self.ast_node.add_field(p)
        return p
Example #2
0
    def create_internal_property(self, name, expr, type):
        """
        Create an internal property for this env spec.
        """
        if expr is None:
            return None

        p = PropertyDef(
            expr, AbstractNodeData.PREFIX_INTERNAL,
            name=names.Name('_{}_{}'.format(name,
                                            next(self.PROPERTY_COUNT))),
            public=False, type=type, ignore_warn_on_node=True
        )
        p.location = getattr(expr, 'location') or self.location
        self.ast_node.add_field(p)
        return p
Example #3
0
    def compute_properties(self, compile_only=False):
        """
        Compute information related to ASTNode's properties. This needs to be a
        global analysis because we want to compute which properties need to be
        dispatching, and this is determined not only by the context of one
        node, but by whether the parent has a property with the same name.
        """

        for pass_fn in PropertyDef.compilation_passes(compile_only):
            for astnode in self.astnode_types:
                for prop in astnode.get_properties(include_inherited=False):
                    if self.verbosity.debug:
                        print 'Running {} on {}'.format(
                            pass_fn.__name__,
                            prop.qualname,
                        )
                    with prop.diagnostic_context():
                        pass_fn(prop)

                # Env specs generate properties, and have some invariants to
                # check after properties have been properly computed. Perform
                # these checks only once and at the proper time, though.
                if (pass_fn == PropertyDef.construct_and_type_expression
                        and astnode.env_spec):
                    astnode.env_spec.check_properties()
Example #4
0
    def _render_field_access(self, p: PropertyDef) -> str:
        """
        Helper to render a simple field access to the property P in the context
        of an environment specification.

        :param p: The property to access. It must accept no explicit argument.
        """
        assert not p.natural_arguments

        with PropertyDef.bind_none(), \
                Self.bind_type(self.ast_node):
            return FieldAccess.Expr(Self.construct_nocheck(), p,
                                    []).render_expr()
Example #5
0
    def _render_field_access(self, p):
        """
        Helper to render a simple field access to the property P in the context
        of an environment specification.

        :param PropertyDef p: The property to access. It must accept no
            explicit argument.
        :rtype: str
        """
        assert not p.explicit_arguments

        with PropertyDef.bind_none(), \
                Self.bind_type(self.ast_node), \
                Env.bind_name('Current_Env'):
            return FieldAccess.Expr(construct(Self), p, []).render_expr()
Example #6
0
    def create_internal_property(self, name, expr, type):
        """
        Create an internal property for this env spec.

        If ``expr`` is None, do not create a property and return None.
        Otherwise, unsugar it.

        :param str name: Lower-case name to use to create this property name.
            Since the property is internal, the name is decorated.
        """
        if expr is None:
            return None

        expr = unsugar(expr)
        p = PropertyDef(expr,
                        AbstractNodeData.PREFIX_INTERNAL,
                        name=names.Name('_{}_{}'.format(
                            name, next(self.PROPERTY_COUNT))),
                        public=False,
                        type=type,
                        ignore_warn_on_node=True)
        p.location = getattr(expr, 'location') or self.location
        self.ast_node.add_field(p)
        return p
Example #7
0
    def _render_field_access(self, p):
        """
        Helper to render a simple field access to the property P in the context
        of an environment specification.

        :param PropertyDef p: The property to access. It must accept no
            explicit argument.
        :rtype: str
        """
        assert not p.explicit_arguments

        with PropertyDef.bind_none(), \
                Self.bind_type(self.ast_node), \
                Env.bind_name('Current_Env'):
            return FieldAccess.Expr(construct(Self), p, []).render_expr()
Example #8
0
    def construct(self):
        """
        Construct a resolved expression for this.

        :rtype: ResolvedExpression
        """
        outer_scope = PropertyDef.get_scope()

        matched_expr = construct(
            self.matched_expr, lambda t: t.is_ast_node or t.is_entity_type,
            'Match expressions can only work on AST nodes or entities: got'
            ' {expr_type} instead')
        is_entity = matched_expr.type.is_entity_type
        matchers = []

        # Check (i.e. raise an error if no true) the set of matchers is valid:

        # * all matchers must target allowed types, i.e. input type subclasses;
        for typ, var, expr in self.matchers:
            if is_entity and typ and not typ.is_entity_type:
                typ = typ.entity
                var._type = typ
                var.local_var.type = typ

            if typ is not None:
                check_source_language(
                    typ.matches(matched_expr.type),
                    'Cannot match {} (input type is {})'.format(
                        typ.dsl_name, matched_expr.type.dsl_name))
            else:
                # The default matcher (if any) matches the most general type,
                # which is the input type.
                var.set_type(matched_expr.type)

            # Create a scope so that match_var is contained in this branch as
            # is not exposed outside in the debug info.
            with outer_scope.new_child() as inner_scope:
                inner_scope.add(var.local_var)
                matchers.append(
                    Match.Matcher(construct(var), construct(expr),
                                  inner_scope))

        # * all possible input types must have at least one matcher. Also warn
        #   if some matchers are unreachable.
        self._check_match_coverage(matched_expr.type)

        return Match.Expr(matched_expr, matchers, abstract_expr=self)
Example #9
0
    def compute_properties(self):
        """
        Compute information related to ASTNode's properties. This needs to be a
        global analysis because we want to compute which properties need to be
        dispatching, and this is determined not only by the context of one
        node, but by whether the parent has a property with the same name.
        """

        for pass_fn in PropertyDef.compilation_passes():
            for astnode in self.astnode_types:
                for prop in astnode.get_properties(include_inherited=False):
                    with prop.diagnostic_context():
                        pass_fn(prop)

                if astnode.env_spec:
                    # Env specs generate properties, and have some invariants
                    # to check after properties have been properly computed.
                    astnode.env_spec.prepare()
Example #10
0
        def create_internal_property(name, expr, type):
            if expr is None:
                return None

            # Set has_implicit_env for these internal properties so that they
            # can use a default environment that the context gives. This
            # default will be the Self_Env of the parent node, which is always
            # the same, regardless of being run in the populate lexical env
            # pass or later on. See Initial_Env_Getter_Fn functions for the
            # code that fetches this default environment.
            p = PropertyDef(expr,
                            AbstractNodeData.PREFIX_INTERNAL,
                            name=names.Name('_{}_{}'.format(
                                name, next(self.PROPERTY_COUNT))),
                            private=True,
                            type=type,
                            has_implicit_env=True)
            result.append(p)
            return p
Example #11
0
        def entity_info_expr(self):
            """
            Return the value of the entity info parameter along, compute its
            value. Return None otherwise.

            :rtype: str|None
            """
            # If the property that this field accesses requires entity info,
            # then the prefix is supposed to be an entity. There are only two
            # exceptions to this: either the entity info is actually optional,
            # either we are out of any property. In these cases, leave the
            # default entity info.
            #
            # See CompileCtx.compute_uses_entity_info_attr for how we check
            # that the assertion always holds.
            if not self.implicit_deref:
                assert (self.node_data.optional_entity_info
                        or PropertyDef.get() is None)
                return None

            # When it is required, entity info comes from the entity, if we're
            # calling the property on an entity.
            return '{}.Info'.format(self.prefix)
Example #12
0
        def __init__(self, prefix_expr, matchers, abstract_expr=None):
            """
            :param ResolvedExpression prefix_expr: The expression on which the
                dispatch occurs. It must be either an AST node or an entity.
            :param list[Match.Matcher] matchers: List of matchers for this
                node.
            """
            assert (prefix_expr.type.is_ast_node
                    or prefix_expr.type.is_entity_type)
            self.prefix_expr = NullCheckExpr(
                prefix_expr, implicit_deref=prefix_expr.type.is_entity_type)

            # Variable to hold the value of which we do dispatching
            # (prefix_expr).
            self.prefix_var = PropertyDef.get().vars.create(
                'Match_Prefix', self.prefix_expr.type)

            # Compute the return type as the unification of all branches
            rtype = matchers[-1].match_expr.type
            for m in matchers:
                rtype = m.match_expr.type.unify(
                    rtype,
                    'Mismatching types in Match expression: got {self} but'
                    ' expected {other} or sub/supertype')
            self.static_type = rtype

            # Wrap each matcher expression so that all capture variables are
            # bound and initialized.
            self.matchers = []
            for m in matchers:
                # Initialize match_var. Note that assuming the code generation
                # is bug-free, this cast cannot fail, so don't generate type
                # check boilerplate.
                let_expr = Let.Expr(
                    [m.match_var],
                    [
                        Cast.Expr(self.prefix_var.ref_expr,
                                  m.match_var.type,
                                  unsafe=True)
                    ],

                    # ... and cast this matcher's result to the Match result's
                    # type, as required by OOP with access types in Ada.
                    (m.match_expr if m.match_expr.type == rtype else Cast.Expr(
                        m.match_expr, rtype)))

                # Now do the binding for static analysis and debugging
                self.matchers.append(
                    Match.Matcher(
                        m.match_var,
                        BindingScope(let_expr, [], scope=m.inner_scope),
                        m.inner_scope))

            # Determine for each matcher the set of concrete AST nodes it can
            # actually match.
            prefix_type = self.prefix_expr.type
            if prefix_type.is_entity_type:
                prefix_type = prefix_type.element_type
            matched_types, remainder = collapse_concrete_nodes(
                (prefix_type.element_type if prefix_type.is_entity_type else
                 prefix_type), [m.match_astnode_type for m in self.matchers])
            assert not remainder
            for matcher, matched in zip(self.matchers, matched_types):
                matcher.matched_concrete_nodes = matched

            super(Match.Expr, self).__init__('Match_Result',
                                             abstract_expr=abstract_expr)
Example #13
0
        def __init__(self,
                     receiver_expr,
                     node_data,
                     arguments,
                     implicit_deref=False,
                     unsafe=False,
                     abstract_expr=None):
            """
            :param ResolvedExpression receiver_expr: The receiver of the field
                access.

            :param langkit.compiled_types.AbstracNodeData node_data: The
                accessed property or field.

            :param list[ResolvedExpression|None] arguments: If non-empty, this
                field access will actually be a primitive call. Each item is a
                ResolvedExpression for an actual to pass, or None for arguments
                to let them have their default value. List list must have the
                same size as `node_data.natural_arguments`.

            :param bool implicit_deref: Whether the receiver is an entity,
                and we want to access a field or property of the stored node.
                In the case of an entity prefix for an AST node field, return
                an entity with the same entity info.

            :param bool unsafe: If true, don't generate the null crheck before
                doing the field access. This is used to avoid noisy and useless
                null checks in generated code: these checks would fail only
                because of a bug in the code generator.

            :param AbstractExpression|None abstract_expr: See
                ResolvedExpression's constructor.
            """
            # When calling environment properties, the call itself happens are
            # outside a property. We cannot create a variable in this context,
            # and the field access is not supposed to require a "render_pre"
            # step.
            p = PropertyDef.get()
            self.simple_field_access = not p
            assert not self.simple_field_access or not implicit_deref

            self.implicit_deref = implicit_deref
            self.unsafe = unsafe

            self.original_receiver_expr = receiver_expr
            self.receiver_expr = (receiver_expr if self.simple_field_access
                                  or self.unsafe else NullCheckExpr(
                                      receiver_expr, implicit_deref))

            self.node_data = node_data

            # Keep the original node data for debugging purposes
            self.original_node_data = node_data

            # If this is a property call, take the root property
            if self.node_data.is_property:
                self.node_data = self.node_data.root_property

            self.arguments = arguments
            if self.arguments is not None:
                assert (len(self.arguments) == len(
                    self.node_data.natural_arguments))

            if isinstance(self.node_data, PropertyDef):
                self.dynamic_vars = [
                    construct(dynvar) for dynvar in self.node_data.dynamic_vars
                ]

            self.static_type = self.node_data.type
            if self.wrap_result_in_entity:
                self.static_type = self.static_type.entity

            # Create a variable for all field accesses in properties. This is
            # needed because the property will return an owning reference, so
            # we need it to be attached to the scope. In other cases, this can
            # make debugging easier.
            super(FieldAccess.Expr, self).__init__(
                None if self.simple_field_access else 'Fld',
                abstract_expr=abstract_expr,
            )
Example #14
0
    def construct(self):
        """
        :rtype: StructExpr
        """
        if self.struct_type.is_ast_node:
            check_source_language(
                not self.struct_type.is_list_type,
                'List node synthetization is not supported for now')
            check_source_language(
                PropertyDef.get().memoized,
                'Node synthetization can only happen inside a memoized'
                ' property')

        # Make sure the provided set of fields matches the one the struct
        # needs.
        def error_if_not_empty(name_set, message):
            check_source_language(
                not name_set,
                ('{}: {}'.format(message, ', '.join(name
                                                    for name in name_set))))

        # Create a dict of field names to fields in the struct type

        def is_required(f):
            if isinstance(f, BuiltinField):
                # BuiltinFields are actually stored fields only for structure
                # types (not for nodes).
                return self.struct_type.is_struct_type

            elif isinstance(f, Field):
                return not f.null

            else:
                return isinstance(f, UserField)

        required_fields = {
            f._name.lower: f
            for f in self.struct_type.get_abstract_node_data()
            if is_required(f)
        }

        error_if_not_empty(
            set(required_fields) - set(self.field_values.keys()),
            'Values are missing for {} fields'.format(
                self.struct_type.dsl_name))
        error_if_not_empty(
            set(self.field_values.keys()) - set(required_fields),
            'Extraneous fields for {}'.format(self.struct_type.dsl_name))

        # At this stage, we know that the user has only provided fields that
        # are valid for the struct type.
        provided_fields = {
            required_fields[name].name: construct(
                value, required_fields[name].type,
                'Wrong type for field {}: expected {{expected}}, '
                'got {{expr_type}}'.format(name))
            for name, value in self.field_values.items()
        }

        expr_cls = (New.NodeExpr
                    if self.struct_type.is_ast_node else New.StructExpr)
        return expr_cls(self.struct_type, provided_fields, abstract_expr=self)