예제 #1
0
def compile_ast_fragment_to_ir(tree,
                               schema,
                               *,
                               anchors=None,
                               path_prefix_anchor=None,
                               location=None,
                               modaliases=None):
    """Compile given EdgeQL AST fragment into EdgeDB IR."""
    ctx = stmtctx.init_context(schema=schema,
                               anchors=anchors,
                               modaliases=modaliases)
    ctx.clause = location or 'where'
    if path_prefix_anchor is not None:
        path_prefix = anchors[path_prefix_anchor]
        ctx.partial_path_prefix = setgen.class_set(path_prefix, ctx=ctx)
        ctx.partial_path_prefix.anchor = path_prefix_anchor
        ctx.partial_path_prefix.show_as_anchor = path_prefix_anchor
    ir_set = dispatch.compile(tree, ctx=ctx)
    try:
        result_type = inference.infer_type(ir_set, ctx.env)
    except errors.QueryError:
        # Not all fragments can be resolved into a concrete type,
        # that's OK.
        result_type = None

    return irast.Statement(expr=ir_set,
                           schema=ctx.env.schema,
                           stype=result_type)
예제 #2
0
def compile_ast_fragment_to_ir(
    tree: qlast.Base,
    schema: s_schema.Schema,
    *,
    options: Optional[CompilerOptions] = None,
) -> irast.Statement:
    """Compile given EdgeQL AST fragment into EdgeDB IR.

    Unlike :func:`~compile_ast_to_ir` above, this does not assume
    that the AST *tree* is a complete statement.  The expression
    doesn't even have to resolve to a specific type.

    Args:
        tree:
            EdgeQL AST fragment.

        schema:
            Schema instance.  Must contain definitions for objects
            referenced by the AST *tree*.

        options:
            An optional :class:`edgeql.compiler.options.CompilerOptions`
            instance specifying compilation options.

    Returns:
        An instance of :class:`ir.ast.Statement`.
    """
    if options is None:
        options = CompilerOptions()

    ctx = stmtctx_mod.init_context(schema=schema, options=options)
    ir_set = dispatch_mod.compile(tree, ctx=ctx)

    result_type: Optional[s_types.Type]
    try:
        result_type = inference_mod.infer_type(ir_set, ctx.env)
    except errors.QueryError:
        # Not all fragments can be resolved into a concrete type,
        # that's OK.
        result_type = None

    return irast.Statement(
        expr=ir_set,
        schema=ctx.env.schema,
        stype=result_type,
        dml_exprs=ctx.env.dml_exprs,
    )
예제 #3
0
def fini_expression(
    ir: irast.Base,
    *,
    ctx: context.ContextLevel,
) -> irast.Command:

    if (isinstance(ir, irast.Set)
            and pathctx.get_set_scope(ir, ctx=ctx) is None):
        ir = setgen.scoped_set(ir, ctx=ctx)

    cardinality = qltypes.Cardinality.AT_MOST_ONE
    if ctx.path_scope is not None:
        # The inference context object will be shared between
        # cardinality and multiplicity inferrers.
        inf_ctx = inference.make_ctx(env=ctx.env)
        # Simple expressions have no scope.
        cardinality = inference.infer_cardinality(
            ir,
            scope_tree=ctx.path_scope,
            ctx=inf_ctx,
        )
        multiplicity: Optional[qltypes.Multiplicity] = None
        if ctx.env.options.validate_multiplicity:
            multiplicity = inference.infer_multiplicity(
                ir,
                scope_tree=ctx.path_scope,
                ctx=inf_ctx,
            )

    # Fix up weak namespaces
    _rewrite_weak_namespaces(ir, ctx)

    if ctx.path_scope is not None:
        ctx.path_scope.validate_unique_ids()

    if isinstance(ir, irast.Command):
        if isinstance(ir, irast.ConfigCommand):
            ir.scope_tree = ctx.path_scope
        # IR is already a Command
        return ir

    volatility = inference.infer_volatility(ir, env=ctx.env)

    if ctx.env.options.schema_view_mode:
        for view in ctx.view_nodes.values():
            if view.is_collection():
                continue

            assert isinstance(view, s_types.InheritingType)
            _elide_derived_ancestors(view, ctx=ctx)

            if not isinstance(view, s_sources.Source):
                continue

            view_own_pointers = view.get_pointers(ctx.env.schema)
            for vptr in view_own_pointers.objects(ctx.env.schema):
                _elide_derived_ancestors(vptr, ctx=ctx)
                ctx.env.schema = vptr.set_field_value(
                    ctx.env.schema,
                    'from_alias',
                    True,
                )

                tgt = vptr.get_target(ctx.env.schema)
                assert tgt is not None

                if (tgt.is_union_type(ctx.env.schema)
                        and tgt.get_is_opaque_union(ctx.env.schema)):
                    # Opaque unions should manifest as std::BaseObject
                    # in schema views.
                    ctx.env.schema = vptr.set_target(
                        ctx.env.schema,
                        ctx.env.schema.get('std::BaseObject',
                                           type=s_types.Type),
                    )

                if not isinstance(vptr, s_sources.Source):
                    continue

                vptr_own_pointers = vptr.get_pointers(ctx.env.schema)
                for vlprop in vptr_own_pointers.objects(ctx.env.schema):
                    _elide_derived_ancestors(vlprop, ctx=ctx)
                    ctx.env.schema = vlprop.set_field_value(
                        ctx.env.schema,
                        'from_alias',
                        True,
                    )

    expr_type = inference.infer_type(ir, ctx.env)

    in_polymorphic_func = (ctx.env.options.func_params is not None
                           and ctx.env.options.func_params.has_polymorphic(
                               ctx.env.schema))

    if (not in_polymorphic_func
            and not ctx.env.options.allow_generic_type_output):
        anytype = expr_type.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                hint='Consider using an explicit type cast.',
                context=ctx.env.type_origins.get(anytype))

    if ctx.must_use_views:
        alias, srcctx = next(iter(ctx.must_use_views.values()))
        raise errors.QueryError(
            f'unused alias definition: {str(alias)!r}',
            context=srcctx,
        )

    result = irast.Statement(
        expr=ir,
        params=list(ctx.env.query_parameters.values()),
        views=ctx.view_nodes,
        source_map=ctx.source_map,
        scope_tree=ctx.env.path_scope,
        cardinality=cardinality,
        volatility=volatility,
        multiplicity=multiplicity,
        stype=expr_type,
        view_shapes={
            src: [ptr for ptr, _ in ptrs]
            for src, ptrs in ctx.env.view_shapes.items()
        },
        view_shapes_metadata=ctx.env.view_shapes_metadata,
        schema=ctx.env.schema,
        schema_refs=frozenset(ctx.env.schema_refs -
                              ctx.env.created_schema_objects),
        schema_ref_exprs=ctx.env.schema_ref_exprs,
        new_coll_types=frozenset(
            t for t in (ctx.env.schema_refs | ctx.env.created_schema_objects)
            if isinstance(t, s_types.Collection) and t != expr_type),
        type_rewrites={s.typeref.id: s
                       for s in ctx.type_rewrites.values()},
    )
    return result
예제 #4
0
def compile_ast_fragment_to_ir(
    tree: qlast.Base,
    schema: s_schema.Schema,
    *,
    options: Optional[CompilerOptions] = None,
) -> irast.Statement:
    """Compile given EdgeQL AST fragment into EdgeDB IR.

    Unlike :func:`~compile_ast_to_ir` above, this does not assume
    that the AST *tree* is a complete statement.  The expression
    doesn't even have to resolve to a specific type.

    Args:
        tree:
            EdgeQL AST fragment.

        schema:
            Schema instance.  Must contain definitions for objects
            referenced by the AST *tree*.

        options:
            An optional :class:`edgeql.compiler.options.CompilerOptions`
            instance specifying compilation options.

    Returns:
        An instance of :class:`ir.ast.Statement`.
    """
    if options is None:
        options = CompilerOptions()

    ctx = stmtctx_mod.init_context(schema=schema, options=options)
    ir_set = dispatch_mod.compile(tree, ctx=ctx)

    result_type: s_types.Type
    try:
        result_type = inference_mod.infer_type(ir_set, ctx.env)
    except errors.QueryError:
        # Not all fragments can be resolved into a concrete type,
        # that's OK.
        # XXX: Is it really? This doesn't come up in the tests at all.
        result_type = cast(s_types.Type, None)

    return irast.Statement(
        expr=ir_set,
        schema=ctx.env.schema,
        stype=result_type,
        dml_exprs=ctx.env.dml_exprs,
        views={},
        params=[],
        globals=[],
        # These values are nonsensical, but ideally the caller does not care
        cardinality=qltypes.Cardinality.UNKNOWN,
        multiplicity=qltypes.Multiplicity.ZERO,
        volatility=qltypes.Volatility.Volatile,
        view_shapes={},
        view_shapes_metadata={},
        schema_refs=frozenset(),
        schema_ref_exprs=None,
        new_coll_types=frozenset(),
        scope_tree=ctx.path_scope,
        source_map={},
        type_rewrites={},
        singletons=[],
    )
예제 #5
0
def compile_ast_fragment_to_ir(
    tree: qlast.Base,
    schema: s_schema.Schema,
    *,
    modaliases: Optional[Mapping[Optional[str], str]] = None,
    anchors: Optional[Mapping[str, Any]] = None,
    path_prefix_anchor: Optional[str] = None,
) -> irast.Statement:
    """Compile given EdgeQL AST fragment into EdgeDB IR.

    Unlike :func:`~compile_ast_to_ir` above, this does not assume
    that the AST *tree* is a complete statement.  The expression
    doesn't even have to resolve to a specific type.

    Args:
        tree:
            EdgeQL AST fragment.

        schema:
            Schema instance.  Must contain definitions for objects
            referenced by the AST *tree*.

        modaliases:
            Module name resolution table.  Useful when this EdgeQL
            expression is part of some other construct, such as a
            DDL statement.

        anchors:
            Predefined symbol table.  Maps identifiers
            (or ``qlast.SpecialAnchor`` instances) to specified
            schema objects or IR fragments.

        path_prefix_anchor:
            Symbol name used to resolve the prefix of abbreviated
            path expressions by default.  The symbol must be present
            in *anchors*.

    Returns:
        An instance of :class:`ir.ast.Statement`.
    """
    ctx = stmtctx.init_context(
        schema=schema, anchors=anchors, modaliases=modaliases)
    if path_prefix_anchor is not None:
        assert anchors is not None
        path_prefix = anchors[path_prefix_anchor]
        assert isinstance(path_prefix, s_types.Type)
        ctx.partial_path_prefix = setgen.class_set(path_prefix, ctx=ctx)
        ctx.partial_path_prefix.anchor = path_prefix_anchor
        ctx.partial_path_prefix.show_as_anchor = path_prefix_anchor

    ir_set = dispatch.compile(tree, ctx=ctx)

    result_type: Optional[s_types.Type]
    try:
        result_type = inference.infer_type(ir_set, ctx.env)
    except errors.QueryError:
        # Not all fragments can be resolved into a concrete type,
        # that's OK.
        result_type = None

    return irast.Statement(expr=ir_set, schema=ctx.env.schema,
                           stype=result_type)
예제 #6
0
파일: stmtctx.py 프로젝트: joe2hpimn/edgedb
def fini_expression(
        ir: irast.Base, *,
        ctx: context.ContextLevel) -> irast.Command:
    # Run delayed work callbacks.
    for cb in ctx.completion_work:
        cb(ctx=ctx)
    ctx.completion_work.clear()

    for ir_set in ctx.env.set_types:
        if ir_set.path_id.namespace:
            ir_set.path_id = ir_set.path_id.strip_weak_namespaces()

    if isinstance(ir, irast.Command):
        if isinstance(ir, irast.ConfigCommand):
            ir.scope_tree = ctx.path_scope
        # IR is already a Command
        return ir

    if ctx.path_scope is not None:
        # Simple expressions have no scope.
        for node in ctx.path_scope.get_all_path_nodes(include_subpaths=True):
            if node.path_id.namespace:
                node.path_id = node.path_id.strip_weak_namespaces()

        cardinality = inference.infer_cardinality(
            ir, scope_tree=ctx.path_scope, env=ctx.env)
    else:
        cardinality = qltypes.Cardinality.ONE

    if ctx.env.schema_view_mode:
        for view in ctx.view_nodes.values():
            if not hasattr(view, 'get_own_pointers'):  # duck check
                continue

            view_own_pointers = view.get_own_pointers(ctx.env.schema)
            for vptr in view_own_pointers.objects(ctx.env.schema):
                ctx.env.schema = vptr.set_field_value(
                    ctx.env.schema,
                    'target',
                    vptr.get_target(ctx.env.schema).material_type(
                        ctx.env.schema))

                derived_from = vptr.get_derived_from(ctx.env.schema)
                if (derived_from is not None
                        and derived_from.get_derived_from(ctx.env.schema)
                        is not None):
                    ctx.env.schema = vptr.set_field_value(
                        ctx.env.schema,
                        'derived_from',
                        derived_from.get_nearest_non_derived_parent(
                            ctx.env.schema,
                        )
                    )

                if not hasattr(vptr, 'get_own_pointers'):
                    continue

                vptr_own_pointers = vptr.get_own_pointers(ctx.env.schema)
                for vlprop in vptr_own_pointers.objects(ctx.env.schema):
                    vlprop_target = vlprop.get_target(ctx.env.schema)
                    ctx.env.schema = vlprop.set_field_value(
                        ctx.env.schema,
                        'target',
                        vlprop_target.material_type(ctx.env.schema))

    expr_type = inference.infer_type(ir, ctx.env)

    if ctx.func is None:
        anytype = expr_type.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                hint='Consider using an explicit type cast.',
                context=ctx.env.type_origins.get(anytype))

    result = irast.Statement(
        expr=ir,
        params=ctx.env.query_parameters,
        views=ctx.view_nodes,
        source_map=ctx.source_map,
        scope_tree=ctx.path_scope,
        cardinality=cardinality,
        stype=expr_type,
        view_shapes=ctx.env.view_shapes,
        view_shapes_metadata=ctx.env.view_shapes_metadata,
        schema=ctx.env.schema,
    )
    return result
예제 #7
0
def fini_expression(
    ir: irast.Base,
    *,
    ctx: context.ContextLevel,
) -> irast.Command:
    # Run delayed work callbacks.
    for cb in ctx.completion_work:
        cb(ctx=ctx)
    ctx.completion_work.clear()

    for ir_set in ctx.env.set_types:
        if ir_set.path_id.namespace:
            ir_set.path_id = ir_set.path_id.strip_weak_namespaces()

    if isinstance(ir, irast.Command):
        if isinstance(ir, irast.ConfigCommand):
            ir.scope_tree = ctx.path_scope
        # IR is already a Command
        return ir

    if ctx.path_scope is not None:
        # Simple expressions have no scope.
        for node in ctx.path_scope.path_descendants:
            if node.path_id.namespace:
                node.path_id = node.path_id.strip_weak_namespaces()

        cardinality = inference.infer_cardinality(
            ir, scope_tree=ctx.path_scope, env=ctx.env)
    else:
        cardinality = qltypes.Cardinality.ONE

    if ctx.env.schema_view_mode:
        for view in ctx.view_nodes.values():
            if view.is_collection():
                continue

            _elide_derived_ancestors(view, ctx=ctx)

            if not isinstance(view, s_sources.Source):
                continue

            view_own_pointers = view.get_pointers(ctx.env.schema)
            for vptr in view_own_pointers.objects(ctx.env.schema):
                _elide_derived_ancestors(vptr, ctx=ctx)

                tgt = vptr.get_target(ctx.env.schema)
                if (tgt.is_union_type(ctx.env.schema)
                        and tgt.get_is_opaque_union(ctx.env.schema)):
                    # Opaque unions should manifest as std::Object
                    # in schema views.
                    ctx.env.schema = vptr.set_target(
                        ctx.env.schema,
                        ctx.env.schema.get('std::Object'),
                    )

                if not hasattr(vptr, 'get_pointers'):
                    continue

                vptr_own_pointers = vptr.get_pointers(ctx.env.schema)
                for vlprop in vptr_own_pointers.objects(ctx.env.schema):
                    _elide_derived_ancestors(vlprop, ctx=ctx)

    expr_type = inference.infer_type(ir, ctx.env)

    in_polymorphic_func = (
        ctx.env.func_params is not None and
        ctx.env.func_params.has_polymorphic(ctx.env.schema)
    )

    if not in_polymorphic_func and not ctx.env.allow_generic_type_output:
        anytype = expr_type.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                hint='Consider using an explicit type cast.',
                context=ctx.env.type_origins.get(anytype))

    if ctx.must_use_views:
        alias, srcctx = next(iter(ctx.must_use_views.values()))
        raise errors.QueryError(
            f'unused alias definition: {alias!r}',
            context=srcctx,
        )

    result = irast.Statement(
        expr=ir,
        params=ctx.env.query_parameters,
        views=ctx.view_nodes,
        source_map=ctx.source_map,
        scope_tree=ctx.path_scope,
        cardinality=cardinality,
        stype=expr_type,
        view_shapes=ctx.env.view_shapes,
        view_shapes_metadata=ctx.env.view_shapes_metadata,
        schema=ctx.env.schema,
        schema_refs=frozenset(
            ctx.env.schema_refs - ctx.env.created_schema_objects),
        new_coll_types=frozenset(
            t for t in ctx.env.created_schema_objects
            if isinstance(t, s_types.Collection) and t != expr_type
        ),
    )
    return result
예제 #8
0
def fini_expression(
    ir: irast.Set,
    *,
    ctx: context.ContextLevel,
) -> irast.Command:

    ir = eta_expand.eta_expand_ir(ir, toplevel=True, ctx=ctx)

    if (isinstance(ir, irast.Set)
            and pathctx.get_set_scope(ir, ctx=ctx) is None):
        ir = setgen.scoped_set(ir, ctx=ctx)

    # exprs_to_clear collects sets where we should never need to use
    # their expr in pgsql compilation, so we strip it out to make this
    # more evident in debug output. We have to do the clearing at the
    # end, because multiplicity/cardinality inference needs to be able
    # to look through those pointers.
    exprs_to_clear = _fixup_materialized_sets(ir, ctx=ctx)
    exprs_to_clear.extend(_find_visible_binding_refs(ir, ctx=ctx))

    # The inference context object will be shared between
    # cardinality and multiplicity inferrers.
    inf_ctx = inference.make_ctx(env=ctx.env)
    cardinality = inference.infer_cardinality(
        ir,
        scope_tree=ctx.path_scope,
        ctx=inf_ctx,
    )

    multiplicity = inference.infer_multiplicity(
        ir,
        scope_tree=ctx.path_scope,
        ctx=inf_ctx,
    )

    # Fix up weak namespaces
    _rewrite_weak_namespaces(ir, ctx)

    ctx.path_scope.validate_unique_ids()

    # Infer cardinalities of type rewrites
    for rw in ctx.type_rewrites.values():
        inference.infer_cardinality(rw, scope_tree=ctx.path_scope, ctx=inf_ctx)

    # ConfigSet and ConfigReset don't like being part of a Set
    if isinstance(ir.expr, (irast.ConfigSet, irast.ConfigReset)):
        ir.expr.scope_tree = ctx.path_scope
        return ir.expr

    volatility = inference.infer_volatility(ir, env=ctx.env)

    if ctx.env.options.schema_view_mode:
        for view in ctx.view_nodes.values():
            if view.is_collection():
                continue

            assert isinstance(view, s_types.InheritingType)
            _elide_derived_ancestors(view, ctx=ctx)

            if not isinstance(view, s_sources.Source):
                continue

            view_own_pointers = view.get_pointers(ctx.env.schema)
            for vptr in view_own_pointers.objects(ctx.env.schema):
                _elide_derived_ancestors(vptr, ctx=ctx)
                ctx.env.schema = vptr.set_field_value(
                    ctx.env.schema,
                    'from_alias',
                    True,
                )

                tgt = vptr.get_target(ctx.env.schema)
                assert tgt is not None

                if (tgt.is_union_type(ctx.env.schema)
                        and tgt.get_is_opaque_union(ctx.env.schema)):
                    # Opaque unions should manifest as std::BaseObject
                    # in schema views.
                    ctx.env.schema = vptr.set_target(
                        ctx.env.schema,
                        ctx.env.schema.get('std::BaseObject',
                                           type=s_types.Type),
                    )

                if not isinstance(vptr, s_sources.Source):
                    continue

                vptr_own_pointers = vptr.get_pointers(ctx.env.schema)
                for vlprop in vptr_own_pointers.objects(ctx.env.schema):
                    _elide_derived_ancestors(vlprop, ctx=ctx)
                    ctx.env.schema = vlprop.set_field_value(
                        ctx.env.schema,
                        'from_alias',
                        True,
                    )

    expr_type = inference.infer_type(ir, ctx.env)

    in_polymorphic_func = (ctx.env.options.func_params is not None
                           and ctx.env.options.func_params.has_polymorphic(
                               ctx.env.schema))

    if (not in_polymorphic_func
            and not ctx.env.options.allow_generic_type_output):
        anytype = expr_type.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                hint='Consider using an explicit type cast.',
                context=ctx.env.type_origins.get(anytype))

    must_use_views = [val for val in ctx.must_use_views.values() if val]
    if must_use_views:
        alias, srcctx = must_use_views[0]
        raise errors.QueryError(
            f'unused alias definition: {str(alias)!r}',
            context=srcctx,
        )

    for ir_set in exprs_to_clear:
        ir_set.expr = None

    group.infer_group_aggregates(ir, ctx=ctx)

    assert isinstance(ir, irast.Set)
    source_map = {
        k: v
        for k, v in ctx.source_map.items()
        if isinstance(k, s_pointers.Pointer)
    }
    result = irast.Statement(
        expr=ir,
        params=list(ctx.env.query_parameters.values()),
        globals=list(ctx.env.query_globals.values()),
        views=ctx.view_nodes,
        source_map=source_map,
        scope_tree=ctx.env.path_scope,
        cardinality=cardinality,
        volatility=volatility,
        multiplicity=multiplicity.own if multiplicity is not None else None,
        stype=expr_type,
        view_shapes={
            src: [ptr for ptr, op in ptrs if op != qlast.ShapeOp.MATERIALIZE]
            for src, ptrs in ctx.env.view_shapes.items()
            if isinstance(src, s_obj.Object)
        },
        view_shapes_metadata=ctx.env.view_shapes_metadata,
        schema=ctx.env.schema,
        schema_refs=frozenset(ctx.env.schema_refs -
                              ctx.env.created_schema_objects),
        schema_ref_exprs=ctx.env.schema_ref_exprs,
        new_coll_types=frozenset(
            t for t in (ctx.env.schema_refs | ctx.env.created_schema_objects)
            if isinstance(t, s_types.Collection) and t != expr_type),
        type_rewrites={s.typeref.id: s
                       for s in ctx.type_rewrites.values()},
        dml_exprs=ctx.env.dml_exprs,
        singletons=ctx.env.singletons,
    )
    return result
예제 #9
0
def fini_expression(ir: irast.Base, *,
                    ctx: context.ContextLevel) -> irast.Command:
    # Run delayed work callbacks.
    for cb in ctx.completion_work:
        cb(ctx=ctx)
    ctx.completion_work.clear()

    for ir_set in ctx.env.set_types:
        if ir_set.path_id.namespace:
            ir_set.path_id = ir_set.path_id.strip_weak_namespaces()

    if isinstance(ir, irast.Command):
        if isinstance(ir, irast.ConfigCommand):
            ir.scope_tree = ctx.path_scope
        # IR is already a Command
        return ir

    if ctx.path_scope is not None:
        # Simple expressions have no scope.
        for node in ctx.path_scope.get_all_path_nodes(include_subpaths=True):
            if node.path_id.namespace:
                node.path_id = node.path_id.strip_weak_namespaces()

        cardinality = inference.infer_cardinality(ir,
                                                  scope_tree=ctx.path_scope,
                                                  env=ctx.env)
    else:
        cardinality = qltypes.Cardinality.ONE

    if ctx.env.schema_view_mode:
        for view in ctx.view_nodes.values():
            if view.is_collection():
                continue

            _elide_derived_ancestors(view, ctx=ctx)

            if not isinstance(view, s_sources.Source):
                continue

            view_own_pointers = view.get_pointers(ctx.env.schema)
            for vptr in view_own_pointers.objects(ctx.env.schema):
                _elide_derived_ancestors(vptr, ctx=ctx)

                if not hasattr(vptr, 'get_pointers'):
                    continue

                vptr_own_pointers = vptr.get_pointers(ctx.env.schema)
                for vlprop in vptr_own_pointers.objects(ctx.env.schema):
                    _elide_derived_ancestors(vlprop, ctx=ctx)

    expr_type = inference.infer_type(ir, ctx.env)

    in_polymorphic_func = (ctx.env.func_params is not None and
                           ctx.env.func_params.has_polymorphic(ctx.env.schema))

    if not in_polymorphic_func and not ctx.env.allow_generic_type_output:
        anytype = expr_type.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                hint='Consider using an explicit type cast.',
                context=ctx.env.type_origins.get(anytype))

    result = irast.Statement(
        expr=ir,
        params=ctx.env.query_parameters,
        views=ctx.view_nodes,
        source_map=ctx.source_map,
        scope_tree=ctx.path_scope,
        cardinality=cardinality,
        stype=expr_type,
        view_shapes=ctx.env.view_shapes,
        view_shapes_metadata=ctx.env.view_shapes_metadata,
        schema=ctx.env.schema,
        schema_refs=frozenset(ctx.env.schema_refs),
    )
    return result