csharplang/meetings/2018/LDM-2018-01-03.md
2018-03-20 09:03:16 -07:00

3.9 KiB

C# Language Design Notes for Jan 3, 2018

Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!

Agenda

  1. Scoping of expression variables in constructor initializer
  2. Scoping of expression variables in field initializer
  3. Scoping of expression variables in query clauses
  4. Caller argument expression attribute
  5. New constraints

Scoping of expression variables in constructor initializer

C(int i) : base(out var x) { ... x ... }

Any reason not to allow it? Not at the language level. There's a design question as to how it's represented in IOperation, but that's not a language question.

Conclusion

Whole body scope.

Scoping of expression variables in field initializer

void M(int x)
{
    int y = N(out var z);
    
    {
        z
    }
}

class C(int x)
{
    int y = N(out var z);
    {

        z
    }

    int w = z;
    }
}

Options:

  1. In scope only in the expression
  2. In scope in the whole field declaration
  3. In scope throughout the class body (in other initializers, methods, etc)

For 3, can it be used before declared? (Locals cannot) Is it visible in member declarations, like method bodies? (Sure seems in scope). What about across partial classes?

class Point(int x, int y)
{
    int length = (sq = x*x + y*y; Sqrt(sq));
}

Instead you can put it in the body of the constructor.

1 or 2 pretty much rule out a future where primary constructor statements are interspersed between member declarations.

Conclusion

We'll go with 1.

Script will have to stay consistent, so declaration variables are lifted to fields, and are visible in subsequent statements.

int x, y;
Point { (x, y) = M(); }

(int x, int y) = M();
int l;
Length { var (x, y) = M(); l = Sqrt(x*x + y*y); }

var (x, y) = M();
int l = Sqrt(x*x + y*y);

Scoping of expression variables in query clauses

from x in e
where M(x, out var y)
select y

from x in e
let (m, y) = (M(x, out var y), y)
select y;

To fully do what the users expect (example 1) we would need to do much more semantic analysis of the query, before we lower. It is very out of touch with the syntactic flavor of queries today.

This does not seem worthwhile, though it probably makes sense.

Conclusion

So we should allow expression variables in queries, but keep them scoped to the individual query clauses. In other words, they aren't range variables. They are normal variables scoped by the lambdas that we translate into.

Separately it will be useful in let (and maybe from) clauses.

Caller expression attribute

The proposal would make it a bit hard for existing APIs: they have existing APIs that would match better than the new ones.

There are solutions to that for API owners:

  1. Take a binary breaking change (that's probably what XUnit would do)
  2. Use different API names (won't be picked up automatically by old code)
  3. Change in ref assemblies only (might for Debug.Assert)

In summary, folks have a decent slate of options.

Conclusion

Fine to accept this one.

Other caller attributes

We are not comfortable with those just yet. We'd need to work on the details. For now we are suspicious of having a record of a lot of different information, and of the number of proposed ones in #87.

QOTD: "What about CallerPoliticalAffiliation?".

Constraints

Propose to allow the Enum and Delegate types as constraints. No special meaning, just allow them. That's a starting point. Adding syntax enum and delegate could be discussed later.

Propose a contextual keyword unmanaged; represent it by putting a ModReq in the constraint of the parameter. It implies the struct constraint.

Reference assemblies are a problem: some tools produce reference assemblies that remove private fields from structs, which already leads to semantic problems in the compiler (definite assignment, allowing pointers to them), and now would lead to more.