aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/Make-lang.in1
-rw-r--r--gcc/rust/backend/rust-compile-context.h16
-rw-r--r--gcc/rust/backend/rust-compile-expr.cc172
-rw-r--r--gcc/rust/backend/rust-compile-expr.h6
-rw-r--r--gcc/rust/backend/rust-compile-pattern.cc218
-rw-r--r--gcc/rust/backend/rust-compile-pattern.h78
-rw-r--r--gcc/testsuite/rust/execute/torture/match1.rs58
-rw-r--r--gcc/testsuite/rust/execute/torture/match2.rs42
8 files changed, 591 insertions, 0 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 391151f..3554982 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -94,6 +94,7 @@ GRS_OBJS = \
rust/rust-lint-marklive.o \
rust/rust-hir-type-check-path.o \
rust/rust-compile-intrinsic.o \
+ rust/rust-compile-pattern.o \
rust/rust-base62.o \
rust/rust-compile-expr.o \
rust/rust-compile-type.o \
diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 6347e76..896d42e 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -233,6 +233,21 @@ public:
return true;
}
+ void insert_pattern_binding (HirId id, tree binding)
+ {
+ implicit_pattern_bindings[id] = binding;
+ }
+
+ bool lookup_pattern_binding (HirId id, tree *binding)
+ {
+ auto it = implicit_pattern_bindings.find (id);
+ if (it == implicit_pattern_bindings.end ())
+ return false;
+
+ *binding = it->second;
+ return true;
+ }
+
void push_fn (tree fn, ::Bvariable *ret_addr)
{
fn_stack.push_back (fncontext{fn, ret_addr});
@@ -326,6 +341,7 @@ private:
std::map<const TyTy::BaseType *, std::pair<HirId, tree>> mono;
std::map<DefId, std::vector<std::pair<const TyTy::BaseType *, tree>>>
mono_fns;
+ std::map<HirId, tree> implicit_pattern_bindings;
// To GCC middle-end
std::vector<tree> type_decls;
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index fa88e48..b77a4d5 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -24,6 +24,9 @@
#include "rust-hir-path-probe.h"
#include "rust-hir-type-bounds.h"
#include "rust-hir-dot-operator.h"
+#include "rust-compile-pattern.h"
+
+#include "fold-const.h"
namespace Rust {
namespace Compile {
@@ -155,6 +158,175 @@ CompileExpr::visit (HIR::DereferenceExpr &expr)
}
void
+CompileExpr::visit (HIR::MatchExpr &expr)
+{
+ // https://gcc.gnu.org/onlinedocs/gccint/Basic-Statements.html#Basic-Statements
+ // TODO
+ // SWITCH_ALL_CASES_P is true if the switch includes a default label or the
+ // case label ranges cover all possible values of the condition expression
+
+ /* Switch expression.
+
+ TREE_TYPE is the original type of the condition, before any
+ language required type conversions. It may be NULL, in which case
+ the original type and final types are assumed to be the same.
+
+ Operand 0 is the expression used to perform the branch,
+ Operand 1 is the body of the switch, which probably contains
+ CASE_LABEL_EXPRs. It may also be NULL, in which case operand 2
+ must not be NULL. */
+ // DEFTREECODE (SWITCH_EXPR, "switch_expr", tcc_statement, 2)
+
+ /* Used to represent a case label.
+
+ Operand 0 is CASE_LOW. It may be NULL_TREE, in which case the label
+ is a 'default' label.
+ Operand 1 is CASE_HIGH. If it is NULL_TREE, the label is a simple
+ (one-value) case label. If it is non-NULL_TREE, the case is a range.
+ Operand 2 is CASE_LABEL, which has the corresponding LABEL_DECL.
+ Operand 3 is CASE_CHAIN. This operand is only used in tree-cfg.c to
+ speed up the lookup of case labels which use a particular edge in
+ the control flow graph. */
+ // DEFTREECODE (CASE_LABEL_EXPR, "case_label_expr", tcc_statement, 4)
+
+ TyTy::BaseType *scrutinee_expr_tyty = nullptr;
+ if (!ctx->get_tyctx ()->lookup_type (
+ expr.get_scrutinee_expr ()->get_mappings ().get_hirid (),
+ &scrutinee_expr_tyty))
+ {
+ translated = ctx->get_backend ()->error_expression ();
+ return;
+ }
+
+ rust_assert (scrutinee_expr_tyty->get_kind () == TyTy::TypeKind::ADT);
+
+ // this will need to change but for now the first pass implementation, lets
+ // assert this is the case
+ TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (scrutinee_expr_tyty);
+ rust_assert (adt->is_enum ());
+ rust_assert (adt->number_of_variants () > 0);
+
+ TyTy::BaseType *expr_tyty = nullptr;
+ if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+ &expr_tyty))
+ {
+ translated = ctx->get_backend ()->error_expression ();
+ return;
+ }
+
+ fncontext fnctx = ctx->peek_fn ();
+ Bvariable *tmp = NULL;
+ bool needs_temp = !expr_tyty->is_unit ();
+ if (needs_temp)
+ {
+ tree enclosing_scope = ctx->peek_enclosing_scope ();
+ tree block_type = TyTyResolveCompile::compile (ctx, expr_tyty);
+
+ bool is_address_taken = false;
+ tree ret_var_stmt = nullptr;
+ tmp = ctx->get_backend ()->temporary_variable (
+ fnctx.fndecl, enclosing_scope, block_type, NULL, is_address_taken,
+ expr.get_locus (), &ret_var_stmt);
+ ctx->add_statement (ret_var_stmt);
+ }
+
+ // lets compile the scrutinee expression
+ tree match_scrutinee_expr
+ = CompileExpr::Compile (expr.get_scrutinee_expr ().get (), ctx);
+
+ // need to access the qualifier field, if we use QUAL_UNION_TYPE this would be
+ // DECL_QUALIFIER i think. For now this will just access the first record
+ // field and its respective qualifier because it will always be set because
+ // this is all a big special union
+ tree scrutinee_first_record_expr
+ = ctx->get_backend ()->struct_field_expression (
+ match_scrutinee_expr, 0, expr.get_scrutinee_expr ()->get_locus ());
+ tree match_scrutinee_expr_qualifier_expr
+ = ctx->get_backend ()->struct_field_expression (
+ scrutinee_first_record_expr, 0, expr.get_scrutinee_expr ()->get_locus ());
+
+ // setup the end label so the cases can exit properly
+ tree fndecl = fnctx.fndecl;
+ Location end_label_locus = expr.get_locus (); // FIXME
+ tree end_label
+ = ctx->get_backend ()->label (fndecl,
+ "" /* empty creates an artificial label */,
+ end_label_locus);
+ tree end_label_decl_statement
+ = ctx->get_backend ()->label_definition_statement (end_label);
+
+ // setup the switch-body-block
+ Location start_location; // FIXME
+ Location end_location; // FIXME
+ tree enclosing_scope = ctx->peek_enclosing_scope ();
+ tree switch_body_block
+ = ctx->get_backend ()->block (fndecl, enclosing_scope, {}, start_location,
+ end_location);
+ ctx->push_block (switch_body_block);
+
+ for (auto &kase : expr.get_match_cases ())
+ {
+ // for now lets just get single pattern's working
+ HIR::MatchArm &kase_arm = kase.get_arm ();
+ rust_assert (kase_arm.get_patterns ().size () > 0);
+
+ // generate implicit label
+ Location arm_locus = kase_arm.get_patterns ().at (0)->get_locus ();
+ tree case_label = ctx->get_backend ()->label (
+ fndecl, "" /* empty creates an artificial label */, arm_locus);
+
+ // not sure if we need to add this to the block or if the CASE_LABEL_EXPR
+ // does this implicitly
+ //
+ // tree case_label_decl_statement
+ // = ctx->get_backend ()->label_definition_statement (case_label);
+
+ // setup the bindings for the block
+ for (auto &kase_pattern : kase_arm.get_patterns ())
+ {
+ tree switch_kase_expr
+ = CompilePatternCaseLabelExpr::Compile (kase_pattern.get (),
+ case_label, ctx);
+ // ctx->add_statement (case_label_decl_statement);
+ ctx->add_statement (switch_kase_expr);
+
+ CompilePatternBindings::Compile (kase_pattern.get (),
+ match_scrutinee_expr, ctx);
+ }
+
+ // compile the expr and setup the assignment if required when tmp != NULL
+ tree kase_expr_tree = CompileExpr::Compile (kase.get_expr ().get (), ctx);
+ if (tmp != NULL)
+ {
+ tree result_reference
+ = ctx->get_backend ()->var_expression (tmp, arm_locus);
+ tree assignment = ctx->get_backend ()->assignment_statement (
+ fnctx.fndecl, result_reference, kase_expr_tree, arm_locus);
+ ctx->add_statement (assignment);
+ }
+
+ // go to end label
+ tree goto_end_label = build1_loc (arm_locus.gcc_location (), GOTO_EXPR,
+ void_type_node, end_label);
+ ctx->add_statement (goto_end_label);
+ }
+
+ // setup the switch expression
+ tree match_body = ctx->pop_block ();
+ tree match_expr_stmt
+ = build2_loc (expr.get_locus ().gcc_location (), SWITCH_EXPR,
+ TREE_TYPE (match_scrutinee_expr_qualifier_expr),
+ match_scrutinee_expr_qualifier_expr, match_body);
+ ctx->add_statement (match_expr_stmt);
+ ctx->add_statement (end_label_decl_statement);
+
+ if (tmp != NULL)
+ {
+ translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ());
+ }
+}
+
+void
CompileExpr::visit (HIR::CallExpr &expr)
{
TyTy::BaseType *tyty = nullptr;
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index 9895540..dc4f909 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -221,6 +221,10 @@ public:
translated
= ctx->get_backend ()->var_expression (var, expr.get_locus ());
}
+ else if (ctx->lookup_pattern_binding (ref, &translated))
+ {
+ return;
+ }
else
{
rust_fatal_error (expr.get_locus (),
@@ -1043,6 +1047,8 @@ public:
void visit (HIR::DereferenceExpr &expr) override;
+ void visit (HIR::MatchExpr &expr) override;
+
protected:
tree compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn,
TyTy::BaseType *receiver,
diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc
new file mode 100644
index 0000000..e634dbd
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-pattern.cc
@@ -0,0 +1,218 @@
+// Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-compile-pattern.h"
+
+namespace Rust {
+namespace Compile {
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern)
+{
+ // lookup the type
+ TyTy::BaseType *lookup = nullptr;
+ bool ok
+ = ctx->get_tyctx ()->lookup_type (pattern.get_mappings ().get_hirid (),
+ &lookup);
+ rust_assert (ok);
+
+ // this must be an enum
+ rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+ TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+ rust_assert (adt->is_enum ());
+
+ // lookup the variant
+ HirId variant_id;
+ ok = ctx->get_tyctx ()->lookup_variant_definition (
+ pattern.get_mappings ().get_hirid (), &variant_id);
+ rust_assert (ok);
+
+ TyTy::VariantDef *variant = nullptr;
+ ok = adt->lookup_variant_by_id (variant_id, &variant);
+ rust_assert (ok);
+
+ mpz_t disciminantl;
+ if (variant->get_variant_type () == TyTy::VariantDef::VariantType::NUM)
+ {
+ mpz_init_set_ui (disciminantl, variant->get_discriminant ());
+ }
+ else
+ {
+ HirId variant_id = variant->get_id ();
+ mpz_init_set_ui (disciminantl, variant_id);
+ }
+
+ tree t = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
+ tree case_low
+ = double_int_to_tree (t, mpz_get_double_int (t, disciminantl, true));
+
+ case_label_expr
+ = build_case_label (case_low, NULL_TREE, associated_case_label);
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::StructPattern &pattern)
+{
+ CompilePatternCaseLabelExpr::visit (pattern.get_path ());
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::TupleStructPattern &pattern)
+{
+ CompilePatternCaseLabelExpr::visit (pattern.get_path ());
+}
+
+// setup the bindings
+
+void
+CompilePatternBindings::visit (HIR::TupleStructPattern &pattern)
+{
+ // lookup the type
+ TyTy::BaseType *lookup = nullptr;
+ bool ok = ctx->get_tyctx ()->lookup_type (
+ pattern.get_path ().get_mappings ().get_hirid (), &lookup);
+ rust_assert (ok);
+
+ // this must be an enum
+ rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+ TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+ rust_assert (adt->is_enum ());
+
+ // lookup the variant
+ HirId variant_id;
+ ok = ctx->get_tyctx ()->lookup_variant_definition (
+ pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+ rust_assert (ok);
+
+ int variant_index = -1;
+ TyTy::VariantDef *variant = nullptr;
+ ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index);
+ rust_assert (ok);
+
+ rust_assert (variant->get_variant_type ()
+ == TyTy::VariantDef::VariantType::TUPLE);
+
+ std::unique_ptr<HIR::TupleStructItems> &items = pattern.get_items ();
+ switch (items->get_item_type ())
+ {
+ case HIR::TupleStructItems::RANGE: {
+ // TODO
+ gcc_unreachable ();
+ }
+ break;
+
+ case HIR::TupleStructItems::NO_RANGE: {
+ HIR::TupleStructItemsNoRange &items_no_range
+ = static_cast<HIR::TupleStructItemsNoRange &> (*items.get ());
+
+ rust_assert (items_no_range.get_patterns ().size ()
+ == variant->num_fields ());
+
+ // we are offsetting by + 1 here since the first field in the record
+ // is always the discriminator
+ size_t tuple_field_index = 1;
+ for (auto &pattern : items_no_range.get_patterns ())
+ {
+ tree variant_accessor
+ = ctx->get_backend ()->struct_field_expression (
+ match_scrutinee_expr, variant_index, pattern->get_locus ());
+
+ tree binding = ctx->get_backend ()->struct_field_expression (
+ variant_accessor, tuple_field_index++, pattern->get_locus ());
+
+ ctx->insert_pattern_binding (
+ pattern->get_pattern_mappings ().get_hirid (), binding);
+ }
+ }
+ break;
+ }
+}
+
+void
+CompilePatternBindings::visit (HIR::StructPattern &pattern)
+{
+ // lookup the type
+ TyTy::BaseType *lookup = nullptr;
+ bool ok = ctx->get_tyctx ()->lookup_type (
+ pattern.get_path ().get_mappings ().get_hirid (), &lookup);
+ rust_assert (ok);
+
+ // this must be an enum
+ rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+ TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+ rust_assert (adt->is_enum ());
+
+ // lookup the variant
+ HirId variant_id;
+ ok = ctx->get_tyctx ()->lookup_variant_definition (
+ pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+ rust_assert (ok);
+
+ int variant_index = -1;
+ TyTy::VariantDef *variant = nullptr;
+ ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index);
+ rust_assert (ok);
+
+ rust_assert (variant->get_variant_type ()
+ == TyTy::VariantDef::VariantType::STRUCT);
+
+ auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+ for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+ {
+ switch (field->get_item_type ())
+ {
+ case HIR::StructPatternField::ItemType::TUPLE_PAT: {
+ // TODO
+ gcc_unreachable ();
+ }
+ break;
+
+ case HIR::StructPatternField::ItemType::IDENT_PAT: {
+ // TODO
+ gcc_unreachable ();
+ }
+ break;
+
+ case HIR::StructPatternField::ItemType::IDENT: {
+ HIR::StructPatternFieldIdent &ident
+ = static_cast<HIR::StructPatternFieldIdent &> (*field.get ());
+
+ tree variant_accessor
+ = ctx->get_backend ()->struct_field_expression (
+ match_scrutinee_expr, variant_index, ident.get_locus ());
+
+ size_t offs = 0;
+ ok
+ = variant->lookup_field (ident.get_identifier (), nullptr, &offs);
+ rust_assert (ok);
+
+ // we are offsetting by + 1 here since the first field in the record
+ // is always the discriminator
+ tree binding = ctx->get_backend ()->struct_field_expression (
+ variant_accessor, offs + 1, ident.get_locus ());
+
+ ctx->insert_pattern_binding (ident.get_mappings ().get_hirid (),
+ binding);
+ }
+ break;
+ }
+ }
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-pattern.h b/gcc/rust/backend/rust-compile-pattern.h
new file mode 100644
index 0000000..e49f75c
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-pattern.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompilePatternCaseLabelExpr : public HIRCompileBase
+{
+ using Rust::Compile::HIRCompileBase::visit;
+
+public:
+ static tree Compile (HIR::Pattern *pattern, tree associated_case_label,
+ Context *ctx)
+ {
+ CompilePatternCaseLabelExpr compiler (ctx, associated_case_label);
+ pattern->accept_vis (compiler);
+ return compiler.case_label_expr;
+ }
+
+ void visit (HIR::PathInExpression &pattern) override;
+
+ void visit (HIR::StructPattern &pattern) override;
+
+ void visit (HIR::TupleStructPattern &pattern) override;
+
+private:
+ CompilePatternCaseLabelExpr (Context *ctx, tree associated_case_label)
+ : HIRCompileBase (ctx), case_label_expr (error_mark_node),
+ associated_case_label (associated_case_label)
+ {}
+
+ tree case_label_expr;
+ tree associated_case_label;
+};
+
+class CompilePatternBindings : public HIRCompileBase
+{
+ using Rust::Compile::HIRCompileBase::visit;
+
+public:
+ static void Compile (HIR::Pattern *pattern, tree match_scrutinee_expr,
+ Context *ctx)
+ {
+ CompilePatternBindings compiler (ctx, match_scrutinee_expr);
+ pattern->accept_vis (compiler);
+ }
+
+ void visit (HIR::StructPattern &pattern) override;
+
+ void visit (HIR::TupleStructPattern &pattern) override;
+
+private:
+ CompilePatternBindings (Context *ctx, tree match_scrutinee_expr)
+ : HIRCompileBase (ctx), match_scrutinee_expr (match_scrutinee_expr)
+ {}
+
+ tree match_scrutinee_expr;
+};
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/testsuite/rust/execute/torture/match1.rs b/gcc/testsuite/rust/execute/torture/match1.rs
new file mode 100644
index 0000000..2393b15
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match1.rs
@@ -0,0 +1,58 @@
+// { dg-output "Foo::A\nFoo::B\nFoo::C x\nFoo::D 20 80\n" }
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+enum Foo {
+ A,
+ B,
+ C(char),
+ D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+ match f {
+ Foo::A => unsafe {
+ let a = "Foo::A\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c);
+ },
+ Foo::B => unsafe {
+ let a = "Foo::B\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c);
+ },
+ Foo::C(x) => unsafe {
+ let a = "Foo::C %c\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, x);
+ },
+ Foo::D { x, y } => unsafe {
+ let a = "Foo::D %i %i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, x, y);
+ },
+ }
+}
+
+fn main() -> i32 {
+ let a = Foo::A;
+ let b = Foo::B;
+ let c = Foo::C('x');
+ let d = Foo::D { x: 20, y: 80 };
+
+ inspect(a);
+ inspect(b);
+ inspect(c);
+ inspect(d);
+
+ 0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match2.rs b/gcc/testsuite/rust/execute/torture/match2.rs
new file mode 100644
index 0000000..4a018c1
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match2.rs
@@ -0,0 +1,42 @@
+// { dg-output "123\n80\n" }
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+enum Foo {
+ C(i32),
+ D { x: i32, y: i32 },
+}
+
+fn inspect(f: Foo) -> i32 {
+ match f {
+ Foo::C(x) => x,
+ Foo::D { x, y } => y,
+ // { dg-warning "unused name .x." "" { target *-*-* } .-1 }
+ }
+}
+
+fn main() -> i32 {
+ let a = Foo::C(123);
+ let b = Foo::D { x: 20, y: 80 };
+
+ let result = inspect(a);
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, result);
+ }
+
+ let result = inspect(b);
+ unsafe {
+ let a = "%i\n\0";
+ let b = a as *const str;
+ let c = b as *const i8;
+
+ printf(c, result);
+ }
+
+ 0
+}