GCC Rust Monthly Report #11 October 2021

Thanks again to Open Source Security, inc and Embecosm for their ongoing support for this project.

Milestone Progress

October, we moved onto control flow two, which was a milestone to focus on adding support for the match statement, enums, attempt closures, and fixing known bugs. Finally, we can attempt to test the compiler out on an actual rust project closeout 2021. The goal of trying the compiler on actual rust code is that we don’t expect gccrs to compile the project yet successfully, but we want to find as many bugs/missing-features as possible in the process and assess the state of the front-end.

During development, I began investigating closures early on as it is a complex one to get right when you consider move semantics and how to encapsulate the scope, overall it seemed ok to tackle this from reading how the C++ works. But I ran into a problem with the type system early on to specify the TypePath for closures since the same TypePath is used for function pointers and closures, which means it’s a particular case that involves the usage of lang-items. This means my work on closures is blocked until we get some support for specifying lang items within the compilers, and I have some patches which are in progress to unblock this.

To continue making progress, we already have a list of known bugs for the goal test case. I have been making my way through them since many of them are test cases from the existing rust test-suite and small extractions of code from libcore all of this feed into getting the compiler into a state where we can attempt to compile Blake3.

The other avenue is that thanks to Mark’s work into enums, I have been able to review it. We have already merged parser, name-resolution, and hir lowering the remaining pieces are type-resolution and code-generation, which he has already done a lot of work here. The key component that makes enum’s interesting is that an Enum is an algebraic data type but one with many variants. In contrast, a struct or tuple struct is an algebraic data type with a single variant. This means that we have one canonical path in the compiler for algebraic data types in general, which should help with static analysis in general. Enums must be merged first before the match statement work since the match statement similar to a switch statement in C/C++ is the simplest case to add error handling to ensure all variants have a match-arm.

This month saw ten contributors from worldwide, Asia, Europe and America, and I want to thank you so much. The compiler is still in an early state; this means there is a lot of low hanging fruit to help fix and a lot of error handling code still to write, never mind language features.

Overall I believe the milestone is on track to attempt to make back half of the lost time from the previous milestone all going well, and I am aiming to take some more vacation time this month as well.

Thank you to everyone who continues to support and work on the compiler.

Monthly Community Call

We will be having our 8th community call as the first Friday of the month:

Hacktoberfest 2021

https://hacktoberfest.digitalocean.com/ we marked our repository as part of the hacktoberfest community event which greatly helped us tackle our long list of technical debt over here: https://github.com/Rust-GCC/gccrs/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-pr

LWN Article

LWN wrote an article covering both GCC codegen and GCC Rust from the recent Linux Plumbers Conference. It seemed to be well received thank you to the author Jonathan Corbet and the readers. You can read the article here: https://lwn.net/Articles/871283/

Completed Activities

  • Bug fix missing code generation at coercion sites PR710
  • Add missing constant folding to array expressions PR725 PR748
  • Remove lambda’s as part of code standards cleanup PR727 PR728
  • Implement new Mutability enum for code cleanup PR729 PR738
  • Update PR template and contributor guidelines for DCO sign-off PR730
  • Add building blocks for Closure’s PR740
  • Implement base62 building blocks for V0 symbol mangling PR747
  • Fix ICE in trait resolution PR752 PR758 PR759 PR760
  • Add new unsafety enum instead of bool fields PR754
  • Use const to enforce pointer ownership within the type checker PR756
  • Support generic associated TypePaths via the type-bounds PR757
  • Fix bootstrap build PR761
  • Add missing coercion code generation for arguments in MethodCallExpr PR762
  • Fix naming of generic function symbols in gimple PR763
  • Update copyright years PR764
  • Code Cleanup PR767 PR768 PR772 PR774
  • Add missing Location info to WhereClauseItems PR769 PR778
  • Refactor boolean polarity into enum PR770
  • Higher Ranked Trait bounds PR771
  • Support const evaluation within blocks PR775
  • Mark rust_assert with no side effects PR780
  • Fix lifetime parser bug PR771

Contributors this month

Overall Task Status

CategoryLast WeekThis WeekDelta
TODO9499+5
In Progress912+3
Completed199234+35
GitHub Issues

Testcases

CategoryLast WeekThis WeekDelta
Passing44684844+376
XFAIL2121
make check-rust

Bugs

CategoryLast WeekThis WeekDelta
TODO2122+1
In Progress43-1
Completed6984+15
GitHub Bugs

Milestone Progress

MilestoneLast WeekThis WeekDeltaStart DateCompletion DateTarget
Data Structures 1 – Core100%100%30th Nov 202027th Jan 202129th Jan 2021
Control Flow 1 – Core100%100%28th Jan 202110th Feb 202126th Feb 2021
Data Structures 2 – Generics100%100%11th Feb 202114th May 202128th May 2021
Data Structures 3 – Traits100%100%20th May 202117th Sept 202127th Aug 2021
Control Flow 2 – Pattern Matching7%55%+48%20th Sept 202129th Nov 2021
Macros and cfg expansion0%0%1st Dec 202128th Mar 2022
Imports and Visibility0%0%29th Mar 202227th May 2022
Const Generics0%0%30th May 202225th Jul 2022
Intrinsics0%0%6th Sept 202130th Sept 2022
GitHub Milestones

Risks

RiskImpact (1-3)Likelihood (0-10)Risk (I * L)Mitigation
Rust Language Changes3721Keep up to date with the Rust language on a regular basis
Going over target dates3515Maintain status reports and issue tracking to stakeholders

Planned Activities

  • Continue lang-items work to unblock closure work
  • Finish work on adding enum support

Detailed changelog

Array Expression Const folding

Constant folding is an interesting thing in Rust which is similar in some regards to c++ constexpr. Constants must be folded as part of the type checking system since Array’s themselves have a constant expression of capacity for example. GCCRS has missing visitor’s for each of the possible constant folding cases. Here we have extended it to ArrayExpressions thanks to Rodrigo Valle.

const SIZE: usize = 14 + 2;
const TEST: [i32; SIZE] = [2; SIZE];

Coercion sites bug

When we have coercion sites such as passing arguments to a function, these are chances for missing conversions.

fn static_dispatch<T: Bar>(t: &T) {
    t.baz();
}

fn dynamic_dispatch(t: &dyn Bar) {
    t.baz();
}

fn main() {
    let a;
    a = &Foo(123);

    static_dispatch(a);
    dynamic_dispatch(a);
}

In this example ‘a’ is of type &Foo which is fine as an argument to static_dispatch but for dynamic dispatch, this needs to be converted into a vtable object. This is the same for the case when we have:

fn main() {
    let a;
    a = &Foo(123);

    let b: &dyn Bar = a;
}

The bug in the compiler is that this coercion_site was not being called for argument passing to implicitly convert the original argument.

Code cleanup

Thanks to all who are cleaning up the coding standards within the compiler this is including new enums such as Mutability which is much easier to use than using boolean flags:

Support generic associated TypePaths

Traits can have associated types like we have seen before in previous reports, but for generic functions for example we can specify bounds on the type ‘T’ in this case which means that TypePath’s need to support looking up associated types via ‘probing’ the bounds of the type to find that the placeholder type ‘A’ exists. Which will get setup with the apropriate projection at monomorphization.

trait Foo {
  type A;
   ...
}

fn test<T:Foo>(a:T) -> T::A { .. }

Fix ICE in autoderef for generic MethodCallExpr

When we have generic functions such as:

pub trait Foo {
    type A;

    fn bar(&self) -> Self::A;
}

fn test_bar<T: Foo>(x: T) -> T::A {
    x.bar()
}

The method call here requires an implict borrow (‘&’) to be able to call bar correctly. The compiler was unable to distinguish the difference between the type parameter T and the expected &self so it was unable to generate the implicit adjustment of adding the borrow but this is now fixed.

Fix bug in typechecking of associated types

pub trait Foo {
    type A;

    fn bar(&self) -> Self::A;
}

struct S(i32);
impl Foo for S {
    type A = i32;

    fn bar(&self) -> Self::A {
        self.0
    }
}

fn test_bar<T: Foo>(x: T) -> T::A {
    x.bar()
}

We had a bug when using associated types within generic functions. The compiler wrongly associated impl’s of traits together for generic types. This means that when you used a Path to call a method this means it would automatically project all of the associated types with the impl we found, which resulted in errors like this which don’t make any sense to the programmer.

test.rs:15:5: error: expected [i32] got [<Projection=::i32>]
   15 |     type A = i32;
      |     ^        ~

The bug here is that when we probe the bounds of a type we don’t have any associated impl’s to project the placeholder associated types.

Ensure arguments to MethodCallExpr emit code for coercion-sites

When we have coercion sites such as passing arguments to a function, these are chances for missing conversions.

struct S;
impl S {
    fn dynamic_dispatch(self, t: &dyn Bar) {
        t.baz();
    }
}

fn main() {
    let a;
    a = &Foo(123);

    let b;
    b = S;

    b.dynamic_dispatch(a);
}

In this example ‘a’ is of type &Foo which is fine as an argument to static_dispatch but for dynamic dispatch this needs to be converted into a vtable object. This is the same for the case when we have:

fn main() {
    let a;
    a = &Foo(123);

    let b: &dyn Bar = a;
}

The bug in the compiler is that this coercion_site was not being called for argument passing to implicitly convert the original argument.

Fix parser handling of generic arguments with a single lifetime

We had a bug in our parser where generic arguments with a single lifetime argument as below was parsed an type bounds object.

trait Foo<'a> {}

trait Bar {
    type Item: for<'a> Foo<'a>;
                        // ^^
}

This was a good bug to find as it initially looked as though it was a type resolution bug, but in the end the parser had a bug.

Initial support for higher ranked trait bounds

We have added support for rusts higher ranked trait bounds using the where syntax. This shows that gccrs is starting to compile some more complex Rust code which is a good sign that the front-end is progressing.

This test case is from the rust-testsuite: https://github.com/rust-lang/rust/blob/master/src/test/ui/higher-rank-trait-bounds/hrtb-fn-like-trait.rs

extern "C" {
    fn printf(s: *const i8, ...);
}

trait FnLike<A, R> {
    fn call(&self, arg: A) -> R;
}

struct S;
impl<'a, T> FnLike<&'a T, &'a T> for S {
    fn call(&self, arg: &'a T) -> &'a T {
        arg
    }
}

fn indirect<F>(f: F)
where
    F: for<'a> FnLike<&'a isize, &'a isize>,
{
    let x = 3;
    let y = f.call(&x);

    unsafe {
        let a = "%i\n\0";
        let b = a as *const str;
        let c = b as *const i8;

        printf(c, *y);
    }
}

fn main() -> i32 {
    indirect(S);

    0
}

The key thing here is that unlike normal TypeBound syntax such as:

fn indirect<F>(f: F:Bound<&isize, &isize>)

We check that the type ‘F’ conforms to the the bound and specify that it must inherit these bounds as well.

Leave a Reply

Your email address will not be published. Required fields are marked *