Code Monkey home page Code Monkey logo

csharpstandard's Introduction

C# language standard

Working space for ECMA-TC49-TG2, the C# standard committee.

This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. For more information, see the .NET Foundation Code of Conduct.

C# Language Specification

C# 9 draft

The branch draft-v9 has Draft PRs and Issues for C# 9.

C# 8 draft

The branch draft-v8 has the evolving draft text for C# 8.

C# 7 standard

The branch standard-v7 has the ECMA C# 7 standard text, in Markdown format. For the official standard, see the ECMA site.

C# 6 standard

The branch standard-v6 has the ECMA C# 6 standard text, in Markdown format. For the official standard, see the ECMA site.

C# 5 standard

The branch standard-v5 has the ECMA C# 5 standard text, converted to Markdown. For the official standard, see the ECMA site.

This version is stored in this branch as a base markdown version to compare with future updated standard texts.

Comments within the standard

There are HTML comments (<!-- comment -->) within the standard for the sake of tooling. Some help in the process of converting the standard to Word, and others are for automated testing purposes.

Some automated test comments refer to error codes that are specific to the Microsoft C# compiler (e.g., "CS0509") to test that compilation fails as expected, where an example presents deliberately-invalid code. These error codes are not part of the standard, and should not be viewed as any kind of compliance check for other compilers.

More broadly, no comments should be regarded as being part of the standard itself.

Admin folder

A home for administrative files.

For now, it contains separate logs for past (V6, V7), present (V8), and future (V9) work going on to add new features.

Tools folder

This folder contains tools related to maintaining and converting the ECMA C# spec (ECMA-334).

GetGrammar

This folder contains an ANTLR grammar-extraction tool and support files.

  • ExtractGrammar.exe - the simple-minded grammar-extraction program. It processes only one md file.

  • GetGrammar.bat - the Windows batch file that invokes ExtractGrammar on each md file of the C# specification that contains ANTLR grammar blocks, in clause order, inserting some md headers and such along the way. The result is a file called grammar.md, which is a direct replacement for that file in the specification repo.

A minor wart: There is an extraneous blank line at the beginning of each of the lexical, syntactic, and unsafe grammars. At a glance, the amount of programming effort probably needed to stop this from happening seems to be huge compared with simply deleting those three lines manually.

MarkdownConverter

This tool is used by the committee to produce a Word format of the standard for submission to ECMA or ISO. This is run on each PR to ensure we can always produce the correct format when needed.

StandardAnchorTags

This tool creates the outline using section numbers, and updates all links to the correct section number. Its purpose is to ensure that all references continue to point to the correct section, and that the table of contents shows the correct section numbers for all sections.

Contributors that add sections should follow the guidance in our contributor guide to ensure that links to new sections are incorporated correctly. This tool is run on each PR in a dry-run mode to ensure that the changes will parse correctly. When a PR is merged, the tool runs to update all section links.

ExampleExtractor and ExampleTester

These two tools work in tandem to test that the examples presented work (or fail, where invalid code is presented) as expected.

ExampleExtractor populates a temporary directory with code and metadata extracted from the standard. ExampleTester then compiles and runs (where applicable) that code. The test-examples.sh script provides an easy way of running both tools together.

.NET Foundation

This project is supported by the .NET Foundation.

Table of contents - C# standard

The README.md file in the standard folder contains a detailed table of contents for the C# standard.

csharpstandard's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

csharpstandard's Issues

15.2.2.2 (and more widely) Unconventional spacing around colons

Currently we have:

abstract class B: A

I think it's more conventional to include a space after the type name:

abstract class B : A

We've talked about this before for things like bracing, where the standard is slightly unconventional but saves space - in this case I'd very much doubt that it will make any lines too long. I want to make the examples as unjarring as possible.

If we agree with the proposal (adding the space) I'll go through the standard and identify which examples need to be changed.

12.3.4 Language again

When an operation is statically bound, the type of a constituent expression (e.g., a receiver, and argument, an index or an operand) is always considered to be the compile-time type of that expression.

This strikes me as incorrect Standard-ese, surely in static binding the type is the compile-time type, not just considered to be.

The phrase "is considered" occurs 4 times in the clause.

Proposal: Remove them all, make the clause definitive.

9.2.7 (and 9.2.8): Floating point types and decimal

I have a pet peeve, that the C# spec refers to float and double as floating point types, but not decimal.

We could fix this by referring to them as floating binary point types, and explicitly calling out that decimal is a floating decimal point type - or we could decide that I'm being way too picky.

(A little history to partly explain my obsession with this: before .NET 2.0, MSDN claimed that decimal was a fixed point type. It took many mails with a documentation maintainer back and forth to persuade them to change this. People still refer to "floating point types" as being imprecise, but decimal being "accurate". It winds me up, and the spec isn't helping matters. Rant over :)

12 Inconsistency of dynamic binding description

Some sections - such as 13.13.1 and 13.11.1 - talk about dynamic binding, usually with the same kind of language.

Other sections - such as 13.14 and 13.15 - don't mention it at all. Do we want to fix this inconsistency?

(The conditional operator in 13.15 is interesting, as the precise behaviour would usually require knowledge of both the second and third operand types, to know whether or not to convert an operand into the overall type. However, for dynamic expressions that would require evaluating both of those operands, against the spirit of the operator. So presumably if either of those operands is dynamic, no conversions are performed? That's the sort of thing we should probably specify...)

12.7.3.2 Ambiguous language

I found this part of the proposed standard hard to read, and the note contradictory:

For each occurrence of a given identifier as a full simple-name (without a type argument list) in an expression or declarator, within the local variable declaration space (§9.3) immediately enclosing that occurrence, every other occurrence of the same identifier as a full simple-name in an expression or declarator shall refer to the same entity. [Note: This rule ensures that the meaning of a name is always the same within a given block, switch block, for-, foreach- or using-statement, or anonymous function. end note]

The rule doesn't ensure that the meaning of a name is always the same within a given block, as this is entirely valid:

{
  {
    int x = 10;
  }
  {
    string x = "other";
  }
}

You could argue that "within a given block [...]" doesn't include nested blocks, but then the "error" example in the standard wouldn't fall foul of the rule.

It seems it's really "within a given block [...] that immediately uses that name" - but I hope we can find a better way of expressing this.

It's worth noting that this rule has been removed from C# 6. While I'd love to propose that we ditch it in the standard one version early, that would feel a little cavalier to say the least...

See also:

15.6.6.2 (and 15.13, 16.4.11) Type initialisation, as reported by CLI and C#

Raised by Nigel in October 2006

"At the April meetings in Geneva I had action items from both TG2 & TG3 (Jim Miller shared my TG3
one) to report back on what the intended story on type initialisation is. The aim was to try to resolve
the issue within four weeks after themeeting so any change required could be made to the Standard.
We didn't get a resolution in time.

I did however investigate the situation. The CLI & C# differ is what they state, and reality is different
again - though MS & Mono sometimes differ it is not in substance. We put a note in the forthcoming
Annotated C# Standard, and I forgot to report back to TG2 & TG3 once the deadline has passed...
So my apologies for forgetting, here it is what I found for the record. Thanks in particular to Jan Kotas for feedback. If we ever produce another edition we can consider what to do with these
discrepancies.

Below is (a) what the CLI spec says, (b) what the C# spec says, and (c) what is actually implemented:

(a) CLI:

The CLI currently states:

  1. If marked BeforeFieldInit then the type's initialiser method is executed at, or sometime
    before, first access to any static field defined for that type.
  2. If not marked BeforeFieldInit then that type's initialiser method is executed at (i.e., is
    triggered by):
  • first access to any static field of that type, or
  • first invocation of any static method of that type

or

  • first invocation of any constructor for that type.
  1. Execution of any type's initialiser method will not trigger automatic execution of any initializer
    methods defined by its base type, nor of any interfaces that the type implements
    For reference types, a constructor has to be called to create a non-null instance. Thus, for
    reference types, the .cctor will be called before instance fields can be accessed and methods
    can be called on non-null instances. For value types, an ""all-zero"" instance can be created
    without a constructor (but only this value can be created without a constructor). Thus for value
    types, the .cctor is only guaranteed to be called for instances of the value type that are not ""allzero"".
    [Note: This changes the semantics slightly in the reference class case from the first
    edition of this standard, in that the .cctor might not be called before an instance method is
    invoked if the 'this' argument is null. The added performance of avoiding class constructors
    warrants this change. end note]
    Which can be summarised as:

BFI then at or before first static field access
!BFI at first static field access, static method call or .ctor call
never on instance field access or instance method call (for structs)

(b) C#:
C# currently says:

17.11:
The execution of a static constructor is triggered by the first of the following events to occur
within an application domain:

  • An instance of the class is created.
  • Any of the static members of the class are referenced.

17.4.5.1:

If a static constructor (§17.11) exists in the class, execution of the static field initialisers
occurs immediately prior to executing that static constructor. Otherwise, the static field
initialisers are executed at an implementation-dependent time prior to the first use of a
static field of that class.

18.3.10:

Static constructors for structs follow most of the same rules as for classes. The execution of
a static constructor for a struct is triggered by the first of the following events to occur within
an application domain:

  • An instance member of the struct is referenced.
  • A static member of the struct is referenced.
  • An explicitly declared constructor of the struct is called.

[Note: The creation of default values (§18.3.4) of struct types does not trigger the static
constructor. (An example of this is the initial value of elements in an array.) end note]

Which can be summarised as:

No static constructor but fields inits => BFI
Class and !BFI at first static field access, static method call or .ctor call
Struct and !BFI at first field (static or instance) access, first method (static or instance) call, or
first .ctor call

(c) What is actually implemented:
(i) CLR & Mono

First Mono C# marks all structs as BFI regardless of whether there is a static constructor or
not. VS 2005 C# plays more freely with BFI timing, often for reasons that are not immediately
obvious. However if at is always read as at or before then there are no substantive
differences between the two.
BFI appears to be interpreted as init may trigger before it would for !BFI but it will trigger (it it
hasn't already) at the places !BFI will. I.e. BFI can still trigger even if there is no static field
access at all.

For classes apart from BFI matches CLI/C# spec.
For structs type init is triggered at (or before) first static field access, first static/instance
method call, or first .ctor call.

This is more than the CLI allows as it triggers on instance method access, but less than C#
requires as it doesn't trigger on instance field access. Also the fact the BFI triggers without
static field access also differs from CLI. So all three are different.

(ii) CLR NGEN
If BFI appears to require field access to trigger.
For classes appears to match CLI/C# spec.
For structs appears to match CLI spec.
So I think NGEN is matching the CLI spec.
No consensus was achieved and what the CLI or C# should be saying/doing, mainly due to the fact
we couldn't do anything about it anymore..."

11.2.9 Reason for conversion from expression rather than type

12.2.9 starts:

An implicit dynamic conversion exists from an expression of type dynamic to any type T.

I seem to remember there are subtle reasons which this isn't:

An implicit dynamic conversion exists from the dynamic type to any type T.

I suspect it's because there'd then be an implicit conversion both ways. Is that the case? Is this worth an explanatory note?

4.14 "implementation" seems to cover both the compiler and execution environment

Current text:

implementation
particular set of software (running in a particular translation environment under particular control options) that performs translation for, and supports execution of methods in, a particular execution environment

I'm not sure what a "translation environment" is, or what this would mean for (say) the existing C++-based compiler implementation.

Is this term deliberately covering both a compiler and a CLI implementation? I think it may well be okay to do that, and let the usage context differentiate, but we should make that decision consciously.

12.7.11.7 No specification of Equals and GetHashCode?

While we certainly don't want to pin down a single implementation of Equals and GetHashCode, I think it would be worth setting expectations around the results of:

var x = new { Name = "Jon" };
var y = new { Name = "Jon" };
var z = new { Name = "Mads" };
Console.WriteLine(x.Equals(y));
Console.WriteLine(x.Equals(z));

I think we'd want to mention:

  • Use of IEquatable<T> for property values, if indeed that's used
  • null handling

2 - requirements around conformance

Section 2 includes:

A conforming implementation of C# is permitted to provide additional types, values, objects, properties, and methods beyond those described in this International Standard, provided they do not alter the behavior of any strictly conforming program.

This is probably stricter than we want... almost any change can alter the behavior of a strictly conforming program in some ways. Consider adding an instance method to Delegate - then anyone creating an extension method on Object with the same name and then calling it on a Delegate would find that the instance method was called instead of their extension method.

Basically I think this is a matter of finding suitable wording once we've decided what level of breakage is acceptable.

8.10 Clarify meaning of "variable"

From the spec:

Execution of a C# program proceeds shall proceed such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable , [...]

What is meant by "variable" at the end of the quoted text compared with "field" in the preceding part of the same sentence? Are we including captured variables (which can't be declared volatile, and aren't fields) in the latter?

10.7 Is there always a conversion from a lambda to an expression tree?

10.7 includes:

If a conversion exists from a lambda expression to a delegate type D, a conversion also exists to the expression tree type Expression<D>.

That immediately raised warning bells with me, knowing that (for example) statement lambdas can't be converted. Now later we do say:

• Not all lambda expressions can be converted to expression trees.

(With a bit more detail.)

These feel like they're in conflict. I think I understand that they're not - the conversion theoretically existing doesn't mean that it's valid, so it exists during overload resolution but then it's checked for validity later, for example... but I'm not sure how well we're representing that.

Perhaps worth a bit of a brainstorm about alternative approaches to expressing this? Again, this may well fall below the bar in terms of things it's worth putting effort into, but I thought I'd raise it.

11.2.2 More justification for identity conversions, please?

I can see why identity conversions are useful in terms of dynamic/object, and to allow explicit casting to the same type, but occasionally they're used in places I wouldn't expect, e.g. in 12.2.7:

From any reference-type to an interface or delegate type T if it has an implicit identity or reference conversion to an interface or delegate type T0 and T0 is variance-convertible (§19.2.4.3) to T.

And likewise in 12.2.8

A value type has a boxing conversion to an interface type I if it has a boxing conversion to an interface type I0 and I0 has an identity conversion to I.

What do those permit that wouldn't be permitted without the identity part? (I suspect a note in the identity clause would be more useful than in the uses of it.)

12.20 Any caveats around constant evaluation?

The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions, except that where run-time evaluation would have thrown an exception, compile-time evaluation causes a compile-time error to occur.

I know this wasn't the case pre-Roslyn for decimal, as the implementation of decimal arithmetic had very slightly different rules to the CLR. (I can dig up the connect issue if necessary.)

I'm wondering whether there are other things we need to be aware of, such as the use of 80-bit arithmetic, different types of signalling NaN etc (probably not the latter, given the lack of expression evaluation). Worth asking wiser heads.

9.5.1 Parameter => argument

From section 10.5.1:

A type-name might identify a constructed type even though it doesn’t specify type parameters directly.

Should this be "even though it doesn't specify type arguments directly"?

I suspect so, but frankly open/closed/constructed/bound/unbound type classification confuses the heck out of me.

15.11.4 (25.1.10 in existing Standard)) Operators in generic classes

Raised by Kurakawa-san in July 2006

25.1.10 Operators in generic classes -- 3rd paragraph goes:

For a conversion operator that converts from a source type S to a target type T, when the
rules specified in 17.9.3 are applied, any type parameters associated with S or T are
considered to be unique types that have no inheritance relationship with other types, and
any constraints on those type parameters are ignored.

However, the following example shows the third operator is an error because C is the
base class of D.

This indicates that the 3rd paragraph tries to mean that

For a conversion operator that converts from a source type S to a target type T, when the
rules specified in 17.9.3 are applied, any type parameters associated with S or T shall
be unique types that have no inheritance relationship with other types.

9.2.8 and 12.9.3 Unclear behavior of the operator % for decimals

10.2.8 The decimal type
The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation.


13.9.3 Division operator
The result of x % y is the value produced by x – (x / y) * y

The question is about evaluation of % operator. Does it evaluate as x – (x / y) * y using exact arithmetics (no rounding) at all intermediate steps, and then rounds the result once? Or rounding happens during each intermediate step?

8.5.4 Protected access clarifications needed

Raised by: Peter Golde

Original comment on spreadsheet said "See Notes", but I could not find the notes. [Rex: See notes at end of this comment]

Raised by Peter Golde on February 13, 2003 originally on 10.5.3

@RexJaeschke said In E3 WD3.1, now 9.5.4.

1.2 Protected access clarifications needed

1.2.1 When is protected access rule enforced?

The specification isn't very clear about when the rules in 10.5.3 (protected access) are taken into account. In particular, are they taken into account during member lookup, which considers only "accessible members", or are they after member lookup has taken place. The specification is not very clear.

Here is an example where it makes a difference:

class A 
{
    public int m = 7;
}

class B: A
{
    new protected string m = "hello";
}

class D: B
{
    static void Main() 
    {
        D d = new D();
        B b = d;

        Console.WriteLine(d.m);
        Console.WriteLine(b.m);   // prints 7 or compiler error
    }
}

The Microsoft C# compiler compiles this and prints "hello" and "7", which we think is the correct behavior.

1.2.2 Access to protected instance constructor

Should a derive class be allowed to call an instance constructor from the base via new().

class B
{
protected B() {}
}

class D: B 
{
    public void f() 
    {
        B b = new B();   //legal?
    }
}

}

This should be seemingly disallowed by analogy to the other protected access rules, but the spec doesn’t call it out. The IL verifiers disallows this code so it should not be valid C#.

12.6, 12.6.2.1 Addition of "and delegate"

13.6 (current 14.4) is titled "Function Members" and has a bulleted list of what a function member is, this does not include delegate.

13.6.2.1's (current 14.4.1) first bullet includes delegate.

The proposal changes the first sentence of 13.6.2.1 to "function member and delegate", which seems to be an attempt to address this, but doesn't handle that this whole clause is titled "Function Members".

Should delegate be in the bulleted list in 13.6 and not added to 13.6.2.1 (as it would no longer need to be)? Or something else - it doesn't seem right as it is.

12.3.4 Type visibility and dynamic binding

Again, this depends on issue ECMA-TC49-TG2/spec#248.

First bullet in 13.3.4:

A constituent expression of compile-time type dynamic is considered to have the type of the actual value that the expression evaluates to at runtime

IIRC, it's a bit more complicated than that, because the calling code may not have access to that type, so dynamic binding to even public members of that type can't happen. Do we need to go into details of that? I understand it went through various iterations within MS, but I'm not sure whether the result is deemed implementation-specific or something that can be nailed down.

12.2.1 "Final result"

After the bulleted list, we have:

The final result of an expression is never a namespace, type, method group or event access. Rather, as noted above, these categories of expressions are intermediate constructs that are only permitted in certain contexts.

It's not clear what "final result" means here. Could we clarify? (I'm not proposing anything, as I'm unsure of the intention.)

8.6 Signatures and overloading - Explicit indexer implementations

The current wording in 8.6 Signatures and overloading:

• The signature of an indexer consists of the type of each of its formal parameters, considered in the order left to right. The signature of an indexer specifically does not include the element type, nor does it include the params modifier that may be specified for the right-most parameter.

Because for indexers (unlike methods) signatures do not include name, it would make the following code invalid:

interface IA
{
    int this[int x] { set; }
}

interface IB
{
    int this[int x] { set; }
}

class C : IA, IB
{
    int IA.this[int x] { set { } }
    int IB.this[int x] { set { } }
}

We should somehow include parts IA. and IB. that appear in indexer implementations into their signatures to make them distinct.

7.5.1 Change description of comments on preprocessor directive lines

"A source line containing a #define, #undef, #if, #elif, #else, #endif, #line, or #endregion directive can end with a single-line comment. Delimited comments (the /* */ style of comments) are not permitted on source lines containing pre-processing directives."

This is misleading in a number of ways, #region should be matched with #endregion, and #pragma should be considered. Where the text is included, it may not be a comment anyway.

Vladimir to consider how best to address.

7.4.5.7: removal of null type

I thought we had an issue on this already, but cannot find one.

In the proposal the null type is removed, rather a null literal is defined to have no type (8.4.5.7).

Now the null type was a device introduced to allow types of expressions to be handled in a uniform way, it's removal requires places which discuss types to also sometimes discuss the null literal - mixing types and literals/expressions. If I recall correctly in a meeting it was suggested that the null type was removed as something that has been added was more complicated with it, but what that was was not recalled at the time and the issue was left to be resolved when whatever that something was came up.

So this is a placeholder for that discussion, when the reason the type was removed is found. (Unless of course I've missed that reason coming up and somebody can record here that the debate has happened and its result.)

12.16.6.3 Highlight the difference between a for loop and a foreach loop

We've already mentioned this elsewhere in the spec, but as 13.16.6.3 explicitly notes that an iteration variable declared by a for loop is effectively declared outside the loop, I think it would be worth reiterating here that a foreach iteration variable is different, with an example.

15.7.11, 10.2.7, 12.6.6.1: Term "returns normally" used but not clearly defined

The term "returns normally" is used in 16.7.11 where it is parenthetically defined:

If execution of the method body of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its caller.

The same term is used in 11.2.7 talking about output parameters:

Every output parameter of a function member or anonymous function shall be definitely assigned (§11.4) before the function member or anonymous function returns normally.

It feels like this should be explicitly defined in a section, then referenced from both places - but I'm not sure which section is best. Possibly 13.6.6.1 (member invocation)?

11.7.2 Object vs reference

We currently have the text:

The exact target object and target method of the delegate are unspecified. In particular, it is unspecified whether the target object of the delegate is null, the this value of the enclosing function member, or some other object.

This feels to me like it confuses objects and references. It immediately makes me go "urgh". On the other hand, fixing it may make the text ugly. Below the bar, or too ugly to keep?

7.2.5 Deviations from grammar ambiguities

If a sequence of tokens can be parsed (in context) as a simple-name (§12.7.3), member-access (§12.7.5), or pointer-member-access (§23.6.3) ending with a type-argument-list (§9.4.2), the token immediately following the closing > token is examined. If it is one of
        ( ) ] : ; , . ? == !=
then the type-argument-list is retained as part of the simple-name, member-access, or pointer-member-access and any other possible parse of the sequence of tokens is discarded. Otherwise, the type-argument-list is not considered part of the simple-name, member-access, or pointer-member-access, even if there is no other possible parse of the sequence of tokens.

In practice, both Roslyn and Mono successfully parse:

using System;

class C
{
    public static bool operator ^(Action a, C b)
    {
        return true;
    }

    void Foo<T, S>()
    {
        var x = Foo<T,S> ^ this;
    }
}

and

using System;

class C
{
    public static bool operator |(Action a, C b)
    {
        return true;
    }

    void Foo<T, S>()
    {
        var x = Foo<T,S> | this;
    }
}

Mono also allows other operators (although Roslyn rejects them):

using System;

class C
{
    public static bool operator +(Action a, C b)
    {
        return true;
    }

    void Foo<T, S>()
    {
        var x = Foo<T,S> + this;
    }
}

Should we update the spec, or just leave these as implementation quirks?

11.2.2 Identity conversions: Floating-point precision orthogonal to type & dynamic <=> object conversions

Currently:

a) Floating-point calculations are allowed to performed in a higher precision than dictated by their "storage" type. (10.2.7)

b) Identity conversions exist so that "T is convertible to T". This allows code like "(int)v" where v is an int, and makes some parts of the formal description easier. (12.2.2)

c) Identity conversion is effectively overloaded to also mean "reduce to storage precision". This means the Standard has to say that an identity conversion usually has no effect at runtime (12.2.2).

The identity conversion does not change the type, as the extended precision type is not treated as a first class citizen, rather precision is treated somewhat as orthogonal to the type. It might be worded better in the current Standard, but it works.

Now in the Proposal we introduce dynamic, along with an identity conversion between object and dynamic - so now an identity conversion can change the type, we have another effective overload of its meaning...

The way dynamic is introduced means that:

  1. quite a few "and dynamic" have been added after "object";

  2. in some places identity conversions are introduced where they did not exist before; and

  3. we also have "For the purposes of conversion, the types object and dynamic are considered equivalent" (12.2.1) – which might seem to reduce the need for the first two.

Maybe all this could be clearer? Could the statement above from 12.2.1 be given more prominence so as to reduce the need for "and dynamic" changes? Could it be clearer that floating-point precision, not type, is changed or could the higher-precision type become first class (in the Standard)?

So does the proposal introduce dynamic in the best way?

(Note clause and floating-point also mentioned in Issue ECMA-TC49-TG2/spec#233)

11.7.1 Exhaustive list of non-expression-tree lambdas?

12.7.1 contains:

Certain lambda expressions cannot be converted to expression tree types: Even though the conversion exists, it fails at compile-time. This is the case if the lambda expression:

  • Has a block body
  • Contains simple or compound assignment operators
  • Contains a dynamically bound expression
  • Is async

The wording suggests this is an exhaustive list. Is it intended to be? Can expression trees cope with pointer types etc? I thought there were various oddities which couldn't be expressed.

9.2.8: Add informative note on IEEE Decimal?

For the existing Standard 11.1.7 the Annotated Standard includes two annotations: the first references the CLI committee's discussion and the decision to allow, but not require, CLI implementations to use IEEE Decimal; the second, written by Mike Cowlishaw, details how an implementation might support the old MS format and the IEEE format automatically.

The CLI Standard itself includes the following:

Note: In Version 1 of this standard, the representation of System.Decimal was well-defined, as follows.
...
In order to allow alternate representations (such as in the update to the IEC floating-point standard, IEC-60559, currently in preparation), the representation has been made unspecified.

The question is whether this allowance in the CLI should be referenced directly in the C# Standard, say as an informative note.

The existing text of 10.2.8 goes to some length to describe the abstract representation and minimum (emphasis added) range. If that is deemed explicit enough then no note is required, if not then one might be indicated.

12.6.2.3 Require a new array for each call with a parameter array and no arguments?

In 13.6.2.3 we have:

In this case, the invocation creates an instance of the parameter array type with a length corresponding to the number of arguments, [...]

and then in an example:

In particular, note that an empty array is created when there are zero arguments given for the parameter array.

This means a compiler can't optimize to reuse a single empty array with the right element type for all calls to a method with a parameter array, when no arguments are passed. We could potentially soften the language here.

Note that C# 6 was potentially going to revisit parameter arrays for IEnumerable<T> parameters, but the feature was at least temporarily dropped - if this whole area is going to be revisited later, we might want to defer any changes.

12.17.3.1 Query expressions aren't always method invocations

Current text:

The C# language does not specify the execution semantics of query expressions. Rather, query expressions are translated into invocations of methods that adhere to the query expression pattern (§13.17.4). Specifically, query expressions are translated into invocations of methods named Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBy, and Cast.

While it's expected that they would be method names, they don't have to be. Bizarre example, selecting from a type via a static property called Select:

using System;

class OddType    
{
    public static Func<Func<int, int>, string> Select
    {
        get { return ignored => "result!"; }
    }
}

class Program
{
    static void Main()
    {
        string result = from t in OddType select t * 2;
        Console.WriteLine(result);
    }
}

The compiler doesn't care about this at all. Worth tweaking the description?

(We should look for "method" everywhere within 13.17 - it comes up in a few places.)

12.13.1 More details on short-circuiting and dynamic evaluation

The standard has the normal indication of dynamic behaviour:

If an operand of a conditional logical operator has the compile-time type dynamic, then the expression is dynamically bound (§13.3.3). In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

But in the case of && or ||, the timing gets a little interesting. The first operand is evaluated before overload resolution is performed

dynamic x = false;
string y = "";        
bool z = x && y;
Console.WriteLine(z); // False

We never do overload resolution because the first operand evaluates to false.

It's not clear to me that the standard predicts this behaviour clearly.

See http://stackoverflow.com/questions/27508991 for the motivation for this issue.

12.6.1 Introduce term "function"

There are various places where it would be useful to encapsulate "function member or anonymous function" into a single term, effectively (and informally!) meaning "a piece of executable code which might have parameters".

Issue 237 highlights one example where "function member" is used where the new term would be appropriate, and I've just spotted that 4.16 ("parameter" definition) should also refer to anonymous functions. (I'll file a new issue for this.)

Proposal 1: we introduce the term "function" to mean "function member or anonymous function"

Proposal 2: this is introduced in section 13.6.1 (where the term "function member" is introduced) just before the closing note, with wording of:

A function is a function member or anonymous function (§13.16).

Proposal 3: Replace all other occurrences of "function member or anonymous function" with "function" (8 occurrences) and "a function member or an anonymous function" with "a function" (1 occurrence)

Ideally we'd then check all occurrences of "function member" to see whether it should actually be function - that's quite a lot of occurrences to check, though...

Global: Use of "Program" vs "Assembly"

There has been an effort on the latest draft to remove the use of the word "assembly" and replace it with "program", for example:

  • see section 7.7.3 where "internal" and "protected internal" are used.
  • see section 8.4.5 for a rename
  • see section 8.4.5.6 for string equality.

Access limited to this program, which is incorrect, because the scope is really limited to an assembly.

12.6.3.1 Optional parameters and parameter arrays supposedly foil type inference

Current text:

If the supplied number of arguments is different than the number of parameters in the method, then inference immediately fails.

That would suggest that:

Foo(10);
...
static void Foo<T>(T value, params string[] args) { }

should fail to compile.

Perhaps we should say that there has to be a corresponding parameter for each argument in the argument list? Basically, it should use 13.6.2 somehow, I'm sure - but it's not clear how.

12.7.3.2 Invariant meaning in blocks should not forbid "Color Color" cases

There is an apparent conflict between 12.7.3.2 and 12.7.5.2. The former appears to disallow the following, while the latter appears to give it specific semantics. The problem is the declaration Color Color; which both uses Color as a simple name to refer to a type, and introduces Color as a simple name to refer to a local.

In practice, C# compilers allow this.

using System;
using System.Collections.Generic;

class Color
{
    static IEnumerable<T> Select<T>(Func<int, T> f) { return null; }

    static void Main()
    {
        Color Color; // Color used as type here
        var q2 = Color.Select(x => x); // Color used as local here.
    }
}

edit by @BillWagner change 13.* to 12.* because section 7 was removed.

8.5.5 Accessibility constraints for types in generic constraints

The list of constraints in 9.5.5 doesn't mention generic constraints, so doesn't (as far as I can tell) forbid something like this:

public class Foo<T> where T : Bar {}
internal class Bar {}

All three compilers we're interested in do prohibit this, so the spec should mention it too.

8.5.2 program -> assembly

This is one of the clauses where "program" is erroneously used to mean "assembly". We previously decided on this so others just need to confirm this is one of the cases then it can be labelled "with editor".

9.2.8 Overflow exceptions when converting *from* decimal?

10.2.8 includes:

and conversions from decimal to the floating-point types might cause loss of precision or overflow exceptions.

Given that the range of each of float and double is greater than the range of decimal, I can't see how a conversion from decimal to float or double could cause an overflow.

I propose we remove "or overflow exceptions".

8.9 Remove dependency on System.GC?

From the spec, not in a note:

The behavior of the garbage collector can be controlled, to some degree, via static methods on the class System.GC. This class can be used to request a collection to occur, finalizers to be run (or not run), and so forth.

That feels like an unnecessary dependency on System.GC. We might want to include it in an example, but that then presumably doesn't require System.GC to be present. (We could present it as "On a system where there is a System.GC type...")

12.11.9 Delegate equality operators

using System;

class X
{
    public static void Main ()
    {
        Action a = null;
        bool res = a == Main; // ok

        bool res1 = a == delegate {}; // Not allowed only method groups can do it
        bool res2 = a == Foo;  // Fails to bind
    }

    static int Foo ()
    {
        return 1;
    }
}

Perhaps we need subsection here to describe method-group specific extension to delegate equality comparisons.

I don't know what was intended purpose of this but C# compiler allows to compare delegates and method groups when one side is a delegate and other side is a method group which can be converted to this delegate. Because method-group is never null the result is actually identical to delegate instance comparison to not-null.

9.5.1: Confusing example

The example at the end of 10.5.1 could do with fleshing out:

A type-name might identify a constructed type even though it doesn’t specify type parameters directly. This
can occur where a type is nested within a generic class declaration, and the instance type of the
containing declaration is implicitly used for name lookup (§16.4.9.7). [Example:

  class Outer<T>
  {
      public class Inner {...}
      public Inner i;             // Type of i is Outer<T>.Inner
  }

end example]

It's not clear to me what the example is of - is it saying that Inner is a constructed type, because its "full name" (in some sense) is Outer<T>.Inner? Or is it only constructed when a type argument is specified, e.g. Outer<string>.Inner?

Assigning to Mads as the most likely candidate to supply more text.

11.7, 12.X Conversions from extension methods to delegate types

This issue was reported by Neal Gafter [email protected].

Consider this code:

using System;

static class S
{
    static void Main()
    {
        string s = "";
        Action a = s.Ext;
    }

    static void Ext(this string s) { }
}

It compiles both on Roslyn and Mono, and creates a delegate to the extension method Ext encapsulating the empty string as a value to be provided as the first argument to the extension method. Unfortunately, the spec does not explain why it should work this way.

Relevant parts of the spec are:

  • §12.7 Method group conversions
  • §13.2.1 Expression classifications — General
  • §13.5.1 Member lookup — General
  • §13.7.5.1 Member access — General
  • §13.7.6.3 Extension method invocations

§12.7 Method group conversions
An implicit conversion (§12.2) exists from a method group (§13.2) to a compatible delegate type. Given a delegate type D and an expression E that is classified as a method group, an implicit conversion exists from E to D if E contains at least one method that is applicable in its normal form (§13.6.4.2) to an argument list constructed by use of the parameter types and modifiers of D

Note that a prerequisite for applying rules from this section is to have an expression that is already classified as a method group. §13.2.1 Expression classifications — General gives the following definition:

An expression is classified as one of the following:
<...>
• A method group, which is a set of overloaded methods resulting from a member lookup (§13.5).

Do we know that s.Ext is a method group? Because s.Ext is syntactically a member-access, we need rules from §13.7.5.1 Member access — General to determine its meaning.

The member-access is evaluated and classified as follows:
• If K is zero and E is a namespace... (NO)
• Otherwise, if E is a namespace... (NO)
• If E is a predefined-type or a primary-expression classified as a type... (NO)
• If E is a property access, indexer access, variable (YES), or value, the type of which is T, and a member lookup (§13.5) of I in T with K type arguments produces a match... (NO)
• Otherwise, an attempt is made to process E.I as an extension method invocation (§13.7.6.3). If this fails, E.I is an invalid member reference, and a binding-time error occurs.

First of all, how an expression of the form E.I could be a method invocation? Where is its argument list? OK, suppose it meant to say

• Otherwise, if E.I occurs as part of an invocation expression E.I(...), an attempt is made to process that expression as an extension method invocation (§13.7.6.3).

But even in this form this bullet would not apply in our case — we do not have an invocation. §13.7.6.3 requires an argument list to start searching for extension methods, but we do not have an argument list. Moreover, §13.7.6.3 does not mention any member lookup, but we know from §13.2.1 that a method group can only result from a member lookup. So, we have to conclude that s.Ext is not a method group and its processing according to §13.7.5.1 must result in a compile-time error.

We could try to save the situation by saying that §12.7 describes processing of an implicit conversion to a delegate type by constructing an imaginary method invocation with an argument list built based on the delegate signature, and this argument list can be used as an argument list required in §13.7.6.3. But as I said initially, the current wording in the spec requires that we already must have a method group as a prerequisite for the processing descibed in §12.7.

We need to fix the spec in several places if we want to break this loop.

9.5.4 Repetition of details in 9.5.1

Between them the first para of 10.5.1 and clause 10.5.4 define unbound generic type, unbound type, constructed type and bound type - which are pairwise similar but slightly different. They also both make the point that an unbound [generic] type can only be used in a typeof expression.

This close repetition and definition of related terms seems clumsy.

The terms [un]bound type are only used once elsewhere in the Standard (§9.5.3, §16.4.2), so the added clause §10.5.4 with its clumsiness, is only to support those two uses.

I think it would be much better if these definitions occurred together and there was no repetition. Also should consider whether the terms are required, given their lack of use, or whether the clauses using them (§9.5.3, §16.4.2) might be written differently.

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.