Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test plan for enhanced using and foreach #28588

Open
26 of 65 tasks
agocke opened this issue Jul 16, 2018 · 2 comments
Open
26 of 65 tasks

Test plan for enhanced using and foreach #28588

agocke opened this issue Jul 16, 2018 · 2 comments
Assignees
Labels
Area-Compilers Test Test failures in roslyn-CI Test-Gap Describes a specific feature or scenario that does not have test coverage
Milestone

Comments

@agocke
Copy link
Member

agocke commented Jul 16, 2018

  • Specification checked in to csharplang.

Feature List

Features

Code Examples

Using declarations

using var fileStream = new FileStream(@"temp\example.txt");

// vs. 

using (var fileStream = new FileStream(@"temp\example.txt")) {
  ...
}

// Works for IAsyncDisposable as well. 
await using var x = new AsyncDisposableType();

Pattern based using for ref struct

ref struct S { 
  public void Dispose() { ... }
}

using var local = new S();

Compiler Secnarios

  • Ensure all features are tied to the C# 8.0 language version
  • Goto and using declaration
    • Goto can't jump over a using declaration unless it also jumps over the closing brace

      { 
          { 
              goto target1; // error
              goto target2; // okay
          }
          using var fileStream = new FileStream(...);
      
          target1:
          ...
      }
      target2:
      ...
    • goto backwards over a using declaration must also cross the opening brace

    • goto can jump around inside a block when it doesn't cross using declaration

  • pattern based using on ref struct
    • Follows the pattern based lookup specification
      • Consider extension methods
      • Consider generic methods where inference is possible
    • Applies to enumerators inside a foreach block (both classic and await foreach)
    • ensure a ref struct respects the lifetime of the initializer
      ref struct S {
          public Span<int> field;
          public void Dispose() { } 
      }
      
      S M() { 
          Span<int> span = stackalloc ... 
          using S local = new S() { field = span; }
          return local; // error 
      }
  • using declaration restrictions
    • prohibit ref and ref readonly in a using declaration
    • test in conjunction with const: using const ... (should be binding error), const using ... (parsing error?)
    • test without initializer: using MyDisposable local;
    • test with non-disposable type
    • test with missing well-known IDisposable type (see uses of Compilation.MakeTypeMissing(WellKnownType) API)
    • test with missing well-known IAsyncDisposable type.
    • test await using declaration only legal in async method.
    • test can't use on a field or parameter
    • test disallowed in case label (unless inside an explicit block)
    • test in scripting
    • test that dynamic is allowed
  • using declaration usage cases
    • test with null / default value
    • test with lambdas or local functions that capture using variables and non-using variables
    • variable is not assignable
    • test can't use as ref or out.
    • possible to use a struct variable as ref readonly or in
      struct S : IDisposable { 
          public void Dispose() { ... }
      }
      
      using S local = new S();
      ref readonly rr1 = ref local; // okay
      ref rr2 = ref local; // error
    • local functions and definite assignment
      public void M() {
          // legal because there isn't a definite assignment issue
          Local();
          
          using FileStream x;
          
          void Local()
          {
              x = null; // error is it's not assignable
              x.ToString();
          }
      }
    • order of dispose is reverse of declaration order
    • multi-variable declarator dispose order
    • multi-variable declarator dispose order in face of exception (all finally executed)
    • test inside an iterator
    • test inside an async method
  • Semantic Model
    • Getting the awaiter info from a using declaration
    • Basic semantic information on the local from the API
  • Consider impact on F#
  • ref struct with Dispose() method should work if it returns Task or ValueTask (task-like)
    • same for DisposeAsync()
  • test scripting (should disallow using-declaration, maybe some additional complications)
  • check that using-declaration logic checks AwaitUsingAndForeachAddsPendingBranch flag (context)
  • test ref struct with Task? DisposeAsync() method in await using (declaration or statement). Should produce a null warning
  • test async using-declaration in async-iterator method
    • two async using-declarations with a yield return in between
  • test obsolete Dispose method on a ref struct in using or in foreach

Productivity

  • Find all references (moved all FAR issues into FindAllReferences should handle Dispose referenced in using and foreach statements #28228)
    • Finds Dispose method usage in using declaration
    • Finds Dispose / AsyncDispose method usage in foreach statements
  • InlineVariable should not trigger for a using declaration variable (replaced by issue InlineVariable should not trigger for a using declaration #35645)
  • Extract method should be disabled for blocks that have a using declaration (replaced by issue Extract Method should understand using declarations #35646)
  • ENC
    • Verify it's functional for pattern based dispose in foreach
    • Using declarations going to be tricky. Mainly because adding “using” to a variable declaration implies
      wrapping the containing scope in a finally block. We need to be careful where to place the sequence point that is
      currently emitted for ‘}’ of such scope. EnC is especially concerned about all exception handling blocks and needs
      to be aware of them.)
  • test MakeMethodAsync on using-declaration.
  • verify sequence points and debugging experience
  • Extras
    • fixer to use the right kind of declaration (await using or using) depending on the kind of disposable
    • refactoring to convert using-statement to using-declaration. (done)

Open LDM Issues

  • Syntax for using var and IAsyncDisposable (await using var x = ...)?
  • Should extension dispose be invoked for null values ( writing x.Dispose() would call it, GetPinnableReference() for fixed doesn't)
  • Nullable-value types in using (should we fail if there isn't a lifted extension, or should we check null and call the underlying type if not)
  • Handle the jump forward 'goto' case. Should we disallow labels in a block after a using var (you can't jump into a using statement today as the scope is different)

Proposal: dotnet/csharplang#1703
Championed issue (with LDM history): dotnet/csharplang#1174

@agocke agocke added Area-Compilers Test Test failures in roslyn-CI labels Jul 16, 2018
@agocke agocke self-assigned this Jul 16, 2018
@jcouv jcouv added this to the 16.0 milestone Jul 17, 2018
@agocke agocke assigned chsienki and unassigned agocke and fayrose Aug 7, 2018
@jcouv jcouv changed the title Test plan for using var Test plan for enhanced using and foreach Oct 12, 2018
@RikkiGibson
Copy link
Contributor

Try the following test case for relaxed .Dispose in foreach

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public struct MyStruct : IEnumerable
    {
        public IEnumerator GetEnumerator() => Enumerable.Empty<object>().GetEnumerator();
    }

    public static class MyStructExtensions
    {
        public static void Dispose(this ref MyStruct s)
        {
            Console.WriteLine("Hello");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach (object elem in default(MyStruct))
            { }
            Console.WriteLine("After foreach");
        }
    }
}

@jcouv
Copy link
Member

jcouv commented May 10, 2019

FYI I went through the IDE scenarios and marked some bullets as verified, and replaced some others with discrete issues.

@jinujoseph jinujoseph modified the milestones: 16.0, 16.3 Jun 9, 2019
@gafter gafter modified the milestones: 16.3, 16.4 Aug 4, 2019
@jaredpar jaredpar modified the milestones: 16.4, Compiler.Next Sep 9, 2019
@gafter gafter added the Test-Gap Describes a specific feature or scenario that does not have test coverage label Jan 2, 2020
@jaredpar jaredpar modified the milestones: Compiler.Next, Backlog Sep 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Test Test failures in roslyn-CI Test-Gap Describes a specific feature or scenario that does not have test coverage
Projects
None yet
Development

No branches or pull requests

8 participants