Git Product home page Git Product logo

gafter / roslyn Goto Github PK

View Code? Open in Web Editor NEW

This project forked from dotnet/roslyn

2.0 2.0 0.0 1.77 GB

The .NET Compiler Platform ("Roslyn") provides open-source C# and Visual Basic compilers with rich code analysis APIs.

License: MIT License

PowerShell 0.11% C# 71.70% Shell 0.05% C++ 0.01% F# 0.01% Batchfile 0.01% 1C Enterprise 0.10% Dockerfile 0.01% CMake 0.01% Visual Basic .NET 28.03% Vim Snippet 0.01%

roslyn's People

Contributors

333fred avatar agocke avatar akhera99 avatar alekseyts avatar allisonchou avatar cosifne avatar cston avatar cyrusnajmabadi avatar dibarbet avatar dotnet-bot avatar dotnet-maestro[bot] avatar dpoeschl avatar dustincampbell avatar gafter avatar genlu avatar heejaechang avatar jaredpar avatar jasonmalinowski avatar jcouv avatar jmarolf avatar joerobich avatar mastr11 avatar mavasani avatar rikkigibson avatar ryzngard avatar sharwell avatar tannergooding avatar tmat avatar vsadov avatar youssef1313 avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

roslyn's Issues

Refactor target-typing of existing constructs

From dotnet#40604 (comment)
I would like to consider adding the following forms to those that require a conversion to a type:

  • Method group
  • (unbound) Lambda expression
  • null literal (except in specific contexts)
  • throw expression
  • tuple expression (when it has no natural type)

That work doesn't belong on this branch, but is something we could do later after merging the target-typed conditional expression implementation to master.

It could likely simplify the compiler a lot because we have to handle these kinds of bound nodes specially in lots of different places in the code.

Records design for "empty" abstract records.

On 2016-02-10 the C# language design committee held a design review where we discussed our developing plan for records, and we got some feedback on suggested directions for the design. The design notes will be published separately. Enclosed is the outline of a design for records that illustrates the main points for one possible approach that is consistent with the design feedback. This is not intended to be a formal specification; we'll write that once we have agreement on the basic approach.

Design Approach

The basic approach here is that record declarations must be either sealed or abstract. Abstract record declarations have no state (by default), and do not implement object.Equals or other equality methods. Abstract records can be seen as an optional feature of this approach; it would be possible for us to only do the sealed version, with the possibility of expanding to support the abstract version in the future if the need is seen to arise. Nevertheless, we'll start with a description of the syntax and semantics for abstract records, as the sealed record semantics will refer back to it when we describe the meaning of inheritance.

While you can think of a "record" as a sequence of named values, it should map to ordinary C# in a straightforward way. There should be as little "magic" as possible. If you write the "equivalent" C# for a record declaration, it should be indistinguishable to the language from the record declaration. Consequently when people "fall off the cliff" of what records provide by default (i.e. their use case is slightly different), they can simply resort to using ordinary class or struct declarations. It would be nice if most examples of "falling off the cliff" could be solved by providing only those members that need to be different from what the compiler would provide by default. For example, if you want one of the properties to be mutable instead of read-only, you just declare it yourself.

A few of the desired features

  • It should work as well for classes and structs (reference records and value records)
  • Record members are read-only properties (by default)
  • A record type implicitly supports a positional pattern for creation.
  • A record type implicitly supports a positional pattern for pattern-matching.
  • A record type implicitly supports a mechanism for functional mutation, or "with"-ing - that is, creating a new instance of "the same type" but with selected members replaced by given values.
  • A record type supports a number of forms of equality, including object.Equals, object.GetHashCode, and IEquality<T>. These should be simple, reflexive, transitive, symmetric, etc.

The benefit of this approach over other proposed approaches (specifically, those that place concrete data in an abstract record) is that there is no need for a canonical ordering of record members to be recorded in metadata as part of the meaning of the type. At the advice of the design review, we have done away with the concept of a record being defined as such a canonical ordering, and we define it very simply by translation into normal C# (with one small extension). The question of "how does the compiler know" how to match things up from one place to another does not arise in this formulation. Any such matching up is done purely locally. Still, a consequence of this expansion is that the record declaration construct can be thought of and can be used as if it is defined in terms of a canonical ordering of its members.

We will ignore IEquality<T> and GetHashCode for this.

Abstract records

An abstract record declaration defines an API that must be satisfied by classes that implement it. Taking our running example, given the declaration

public abstract Person(string FirstName, string LastName);

we would expand it into

public abstract class Person
{
    public abstract string FirstName { get; }
    public abstract string LastName { get; }
    public abstract Person With(
        string FirstName = this.FirstName,
        string LastName = this.LastName);
    public void GetValues(out string FirstName, out string LastName)
    {
        FirstName = this.FirstName;
        LastName = this.LastName;
    }
}

Not shown is the usual compiler-generated default constructor.

Here we see the proposed small feature in the parameter default values of the With method: "caller receiver property". When the caller omits an argument for a parameter declared in this manner, the compiler generates, in the caller, an access to the property specified in the default value.

Sealed records

A sealed record declaration defines a concrete record type, which may extend a class and/or a set of interfaces. Given the declaration

public sealed class Student(int Id, string FirstName, string LastName, decimal Gpa) : Person;

we would expand it into

public sealed class Student
{
    public int Id { get; } // 1
    public override string FirstName { get; } // 2
    public override string LastName { get; }
    public decimal Gpa { get; }
    public Student(int Id, string FirstName, string LastName, decimal Gpa) // 3
    {
        this.Id = Id;
        this.FirstName = FirstName;
        this.LastName = LastName;
        this.Gpa = Gpa;
    }
    public override Person With(
        string FirstName = this.FirstName,
        string LastName = this.LastName) // 4
            => new Student(Id, FirstName, LastName, Gpa);
    public new Student With(
        int Id = this.Id,
        string FirstName = this.FirstName,
        string LastName = this.LastName,
        decimal Gpa = this.Gpa) // 5
            => new Student(Id, FirstName, LastName, Gpa);
    public void GetValues(
        out int Id,
        out string FirstName,
        out string LastName,
        out decimal Gpa) // 6
    {
        Id = this.Id;
        FirstName = this.FirstName;
        LastName = this.LastName;
        Gpa = this.Gpa;
    }
    public override Equals(object that) // 7
    {
        Student other = that as Student;
        return other != null &&
            Equals(Gpa, other.Gpa) &&
            Equals(FirstName, other.FirstName) &&
            Equals(LastName, other.LastName) &&
            Equals(Gpa, other.Gpa);
    }
}

Now we have the question: how did the compiler know to produce each of these members in the form it did?

  1. The compiler generates a simple property corresponding to each declared element (i.e. in the header) of the record declaration.
  2. That property is declared override if a virtual or abstract property of the same name, type, and access is inherited.
  3. The constructor simply initializes the backing store (or invokes the setter) for each of those properties.
  4. Every inherited virtual With method is overridden if its return type is a supertype of the current type and every one of its parameters has a default value of the form this.X where X is an overridden property according to (2). The implementation shall simply be an invocation of the "primary" constructor from (3).
  5. The generated With method similarly has a parameter for each record element, and invokes the primary constructor.
  6. The generated GetValues method similarly has an out parameter for each record element, and initializes it from the corresponding generated property.
  7. The generated Equals method is the obvious and straightforward implementation.

Note that there are no hard problems here. The mapping from the record declaration to the generated code is straightforward. The most complex mapping is for the implementation of an inherited With method (4).

Advantages

  • The mapping from a record declaration to (nearly) ordinary C# is probably as simple as it can be.
  • The only "magic" is a new feature, which could probably stand on its own.

Disadvantages

The only disadvantages arise in the presence of abstract records:

  • Might not model how you would handle an abstract record by hand, where you would likely place shared state in the abstract record along with a constructor to initialize the backing fields.
  • There is no "constructor" (but the default one) for an abstract record. So while we're not sure "records" is the right name for this feature, this design would preclude us from calling them "primary constructors".
  • As a result of that, it will not be possible for users to place code into field initializers of an abstract record, and reference the "parameters" of the record in that field initializer. On the other hand, one can write a constructor explicitly, and place any needed code there.
  • Properties declared in an abstract record would not be accessed as efficiently, because the invocation of the property is a virtual dispatch.

Recommendation

My first preference would be to support only the concrete (sealed) version of record declarations. But if we do intend to support abstract record declarations, I don't think there is a mapping as simple to understand as this one.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.