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

Validate generic arguments in using static directives #30737

Merged

Conversation

RikkiGibson
Copy link
Contributor

Resolves #30726

Looks like there is a separate Validate() phase in Imports which takes place after binding, probably because attempting certain validations during binding can result in a stack overflow.

This PR adds to the Imports.Validate method to validate constraints on using static. It was already validating constraints on using aliases which explains what we were seeing before @AlekseyTs.

@RikkiGibson RikkiGibson requested review from jaredpar, AlekseyTs and a team October 24, 2018 23:01
public void UsingStaticGenericConstraint()
{
var code = @"
using static Test<System.String>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the diagnostic we produce will squiggle all of Test<System.String> instead of just System.String. This is maybe not perfect (why not squiggle the particular type argument that's wrong?) but shouldn't impact usability much.

var typeSymbol = (TypeSymbol)@using.NamespaceOrType;
var corLibrary = typeSymbol.ContainingAssembly.CorLibrary;
var conversions = new TypeConversions(corLibrary);
typeSymbol.CheckAllConstraints(conversions, @using.UsingDirective.Name.Location, semanticDiagnostics);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's an NRE in here.

Copy link
Contributor Author

@RikkiGibson RikkiGibson Oct 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like UsingDirective can be null in scripting scenarios? It seems like a location is always necessary in order to provide diagnostics. Not sure what to do here when @using.UsingDirective is null.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like UsingDirective can be null in scripting scenarios? Do I need to supply an alternate location in such cases?

We usually use NoLocation.Singleton in situations when there is no specific syntax to refer to.


In reply to: 227996503 [](ancestors = 227996503)

foreach (var @using in Usings)
{
// Check if `using static` directives meet constraints.
if (@using.NamespaceOrType.IsType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth explicitly checking if this is a static using? Today yes when NamespaceOrType.IsType is true then it's a using static. That's possibly something that is not going to be true in the future though. Think it might benefit from a more explicit check. Possibly add a property to NamespaceOrTypeUsingDirective called IsStatic that does this under the hood.

Copy link
Contributor

@AlekseyTs AlekseyTs Oct 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth explicitly checking if this is a static using?

It feels like it shouldn't matter whether the using is static or not, the constraints should be satisfied anyway.


In reply to: 227993769 [](ancestors = 227993769)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not certain, but I'm leaning toward no because it seems like any conceivable feature that involves referencing a type in a using directive should still have the compiler check constraints on that type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

if (@using.NamespaceOrType.IsType)
{
var typeSymbol = (TypeSymbol)@using.NamespaceOrType;
var corLibrary = typeSymbol.ContainingAssembly.CorLibrary;
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var corLibrary = typeSymbol.ContainingAssembly.CorLibrary; [](start = 20, length = 58)

I think it is more appropriate to request CorLibrary from _compilation.SourceAssembly rather than from the assembly that defines the type. The type name in the using was resolved in context of the _compilation. We will also be able to share a single TypeConversions instance to check all types. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2fb9e18

@RikkiGibson RikkiGibson requested a review from a team October 24, 2018 23:55
// (2,14): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Test<T>'
// using static Test<System.String>;
Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "Test<System.String>").WithArguments("Test<T>", "T", "string").WithLocation(2, 14));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} [](start = 8, length = 1)

Consider testing constraint errors in containing types and nested types as well.

using static A<A<int>[]>.B;

class A<T> where T : class
{
    internal static class B { }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 53b307e

{
var typeSymbol = (TypeSymbol)@using.NamespaceOrType;
var corLibrary = _compilation.SourceAssembly.CorLibrary;
var conversions = new TypeConversions(corLibrary);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to recreate conversions for every type or can it be re-used here? Believe it can be re-used as it's the same for every iteration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. There's no reason to believe it will change per-using.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that basically the same conversions object is created in order to check constraints on using aliases. Any benefit in trying to lazy-load it on the compilation or something and reuse it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that basically the same conversions object is created in order to check constraints on using aliases. Any benefit in trying to lazy-load it on the compilation or something and reuse it?

It is probably safe to use Conversions cached on a compilation in all these places


In reply to: 228274243 [](ancestors = 228274243)

@@ -2070,8 +2070,11 @@ AbstractClass Test()
}";
var comp = CreateCompilation(text);
comp.VerifyDiagnostics(
// (2,14): error CS0311: The type 'object' cannot be used as type parameter 'T' in the generic type or method 'Crash<T>'. There is no implicit reference conversion from 'object' to 'CrashTest.Crash<object>.AbstractClass'.
// using static CrashTest.Crash<object>;
Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "CrashTest.Crash<object>").WithArguments("CrashTest.Crash<T>", "CrashTest.Crash<object>.AbstractClass", "T", "object").WithLocation(2, 14),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test contains a using static which violates a generic constraint. Before, we emitted a diagnostic on the usage of a class contained within the type imported via using static, but it seems correct to add a diagnostic to the using static directive itself here too.

@RikkiGibson
Copy link
Contributor Author

Determinism test is broken. Weird.

2018-10-25T18:25:05.8119000Z ##[warning]Unable to run "git clean -ffdx" and "git reset --hard HEAD" successfully, delete source folder instead.
2018-10-25T18:25:12.8272801Z ##[error]System.AggregateException: One or more errors occurred. (One or more errors occurred. (Access to the path 'F:\vsagent\9\s\Binaries\Temp\VBCSCompiler\AnalyzerAssemblyLoader\eefc8a0313a344e783a6e8f6c55f7725\9\Microsoft.NetCore.VisualBasic.Analyzers.dll' is denied.)) (Access to the path 'F:\vsagent\9\s\Binaries\Temp\VBCSCompiler\AnalyzerAssemblyLoader\eefc8a0313a344e783a6e8f6c55f7725\9\Microsoft.NetCore.VisualBasic.Analyzers.dll' is denied.) ---> System.UnauthorizedAccessException: Access to the path 'F:\vsagent\9\s\Binaries\Temp\VBCSCompiler\AnalyzerAssemblyLoader\eefc8a0313a344e783a6e8f6c55f7725\9\Microsoft.NetCore.VisualBasic.Analyzers.dll' is denied.
   at System.IO.FileSystem.DeleteFile(String fullPath)
   at System.IO.FileInfo.Delete()
   at Microsoft.VisualStudio.Services.Agent.Util.IOUtil.<>c__DisplayClass9_1.<DeleteDirectory>b__0(FileSystemInfo item)
   at System.Linq.Parallel.ForAllOperator`1.ForAllEnumerator`1.MoveNext(TInput& currentElement, Int32& currentKey)
   at System.Linq.Parallel.ForAllSpoolingTask`2.SpoolingWork()
   at System.Linq.Parallel.SpoolingTaskBase.Work()
   at System.Linq.Parallel.QueryTask.BaseWork(Object unused)
   at System.Linq.Parallel.QueryTask.<>c.<.cctor>b__10_0(Object o)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
   --- End of inner exception stack trace ---
   at System.Linq.Parallel.QueryTaskGroupState.QueryEnd(Boolean userInitiatedDispose)
   at System.Linq.Parallel.SpoolingTask.SpoolForAll[TInputOutput,TIgnoreKey](QueryTaskGroupState groupState, PartitionedStream`2 partitions, TaskScheduler taskScheduler)
   at System.Linq.Parallel.MergeExecutor`1.Execute()
   at System.Linq.Parallel.MergeExecutor`1.Execute[TKey](PartitionedStream`2 partitions, Boolean ignoreOutput, ParallelMergeOptions options, TaskScheduler taskScheduler, Boolean isOrdered, CancellationState cancellationState, Int32 queryId)
   at System.Linq.Parallel.PartitionedStreamMerger`1.Receive[TKey](PartitionedStream`2 partitionedStream)
   at System.Linq.Parallel.ForAllOperator`1.WrapPartitionedStream[TKey](PartitionedStream`2 inputStream, IPartitionedStreamRecipient`1 recipient, Boolean preferStriping, QuerySettings settings)
   at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.ChildResultsRecipient.Receive[TKey](PartitionedStream`2 inputStream)
   at System.Linq.Parallel.ScanQueryOperator`1.ScanEnumerableQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient)
   at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient)
   at System.Linq.Parallel.QueryOperator`1.GetOpenedEnumerator(Nullable`1 mergeOptions, Boolean suppressOrder, Boolean forEffect, QuerySettings querySettings)
   at System.Linq.Parallel.ForAllOperator`1.RunSynchronously()
   at System.Linq.ParallelEnumerable.ForAll[TSource](ParallelQuery`1 source, Action`1 action)
   at Microsoft.VisualStudio.Services.Agent.Util.IOUtil.DeleteDirectory(String path, Boolean contentsOnly, Boolean continueOnContentDeleteError, CancellationToken cancellationToken)
   at Microsoft.VisualStudio.Services.Agent.Util.IOUtil.DeleteDirectory(String path, CancellationToken cancellationToken)
   at Agent.Plugins.Repository.GitSourceProvider.GetSourceAsync(AgentTaskPluginExecutionContext executionContext, RepositoryResource repository, CancellationToken cancellationToken)
   at Agent.Plugins.Repository.CheckoutTask.RunAsync(AgentTaskPluginExecutionContext executionContext, CancellationToken token)
   at Agent.PluginHost.Program.Main(String[] args)
---> (Inner Exception #0) System.UnauthorizedAccessException: Access to the path 'F:\vsagent\9\s\Binaries\Temp\VBCSCompiler\AnalyzerAssemblyLoader\eefc8a0313a344e783a6e8f6c55f7725\9\Microsoft.NetCore.VisualBasic.Analyzers.dll' is denied.
   at System.IO.FileSystem.DeleteFile(String fullPath)
   at System.IO.FileInfo.Delete()
   at Microsoft.VisualStudio.Services.Agent.Util.IOUtil.<>c__DisplayClass9_1.<DeleteDirectory>b__0(FileSystemInfo item)
   at System.Linq.Parallel.ForAllOperator`1.ForAllEnumerator`1.MoveNext(TInput& currentElement, Int32& currentKey)
   at System.Linq.Parallel.ForAllSpoolingTask`2.SpoolingWork()
   at System.Linq.Parallel.SpoolingTaskBase.Work()
   at System.Linq.Parallel.QueryTask.BaseWork(Object unused)
   at System.Linq.Parallel.QueryTask.<>c.<.cctor>b__10_0(Object o)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)<---

@RikkiGibson RikkiGibson force-pushed the using-static-generic-constraint branch from 88e9cca to e723ad8 Compare October 25, 2018 19:42
@RikkiGibson
Copy link
Contributor Author

CI is all good. Please review @AlekseyTs

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (iteration 8)

@RikkiGibson RikkiGibson merged commit dfa9105 into dotnet:master Oct 25, 2018
@RikkiGibson RikkiGibson deleted the using-static-generic-constraint branch October 25, 2018 21:13
wachulski added a commit to wachulski/roslyn that referenced this pull request Oct 31, 2018
…out-if-error-message

* origin/master: (1712 commits)
  User-defined operator checks should ignore differences in tuple member names (dotnet#30774)
  Attempted fix for correctly reporting error CS1069 when using implicit namespaces (dotnet#30244)
  Invert the binding order of InitializerExpressions and CreationExpressions (dotnet#30805)
  Use Arcade bootstrapping scripts (dotnet#30498)
  Ensure that the compilers produce double.NaN values in IEEE canonical form. (dotnet#30587)
  Remove properties set in BeforeCommonTargets.targets
  Fix publishing of dependent projects
  Contributing page: reference Unix build instructions
  Delete 0
  Propagate values from AbstractProject to VisualStudioProjectCreationInfo
  Fix publishing nuget info of dev16.0.x-vs-deps branch
  Revert "Add a SetProperty API for CPS to passing msbuild properties"
  Validate generic arguments in `using static` directives (dotnet#30737)
  Correct 15.9 publish data
  Enable test.
  Do not inject attribute types into .Net modules.
  Add a SetProperty API for CPS to passing msbuild properties
  Revert "add beta2 suffix to dev16 branch"
  Fix references
  Remove commit sha from package versions
  ...
@jcouv jcouv mentioned this pull request Apr 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants