未验证 提交 52444741 编写于 作者: N Neal Gafter 提交者: GitHub

Update `or` combinator narrowed type per LDM (#43419)

Fixes #43377
上级 324e9a79
......@@ -1337,8 +1337,83 @@ void addSubpatternsForTuple(ImmutableArray<TypeWithAnnotations> elementTypes)
permitDesignations = false; // prevent designators under 'or'
var left = BindPattern(node.LeftPattern, inputType, inputValEscape, permitDesignations, hasErrors, diagnostics);
var right = BindPattern(node.RightPattern, inputType, inputValEscape, permitDesignations, hasErrors, diagnostics);
var convertedType = commonType(node, left.ConvertedType, right.ConvertedType, diagnostics) ?? inputType;
// Compute the common type. This algorithm is quadratic, but disjunctive patterns are unlikely to be huge
var narrowedTypeCandidates = ArrayBuilder<TypeSymbol>.GetInstance(2);
collectCandidates(left, narrowedTypeCandidates);
collectCandidates(right, narrowedTypeCandidates);
var convertedType = leastSpecificType(node, narrowedTypeCandidates, diagnostics) ?? inputType;
narrowedTypeCandidates.Free();
return new BoundBinaryPattern(node, disjunction: isDisjunction, left, right, inputType: inputType, convertedType: convertedType, hasErrors);
static void collectCandidates(BoundPattern pat, ArrayBuilder<TypeSymbol> candidates)
{
if (pat is BoundBinaryPattern { Disjunction: true } p)
{
collectCandidates(p.Left, candidates);
collectCandidates(p.Right, candidates);
}
else
{
candidates.Add(pat.ConvertedType);
}
}
TypeSymbol? leastSpecificType(SyntaxNode node, ArrayBuilder<TypeSymbol> candidates, DiagnosticBag diagnostics)
{
Debug.Assert(candidates.Count >= 2);
HashSet<DiagnosticInfo>? useSiteDiagnostics = null;
TypeSymbol? bestSoFar = candidates[0];
// first pass: select a candidate for which no other has been shown to be an improvement.
for (int i = 1, n = candidates.Count; i < n; i++)
{
TypeSymbol candidate = candidates[i];
bestSoFar = lessSpecificCandidate(bestSoFar, candidate, ref useSiteDiagnostics) ?? bestSoFar;
}
// second pass: check that it is no more specific than any candidate.
for (int i = 0, n = candidates.Count; i < n; i++)
{
TypeSymbol candidate = candidates[i];
TypeSymbol? spoiler = lessSpecificCandidate(candidate, bestSoFar, ref useSiteDiagnostics);
if (spoiler is null)
{
bestSoFar = null;
break;
}
// Our specificity criteria are transitive
Debug.Assert(spoiler.Equals(bestSoFar, TypeCompareKind.ConsiderEverything));
}
diagnostics.Add(node.Location, useSiteDiagnostics);
return bestSoFar;
}
// Given a candidate least specific type so far, attempt to refine it with a possibly less specific candidate.
TypeSymbol? lessSpecificCandidate(TypeSymbol bestSoFar, TypeSymbol possiblyLessSpecificCandidate, ref HashSet<DiagnosticInfo>? useSiteDiagnostics)
{
if (bestSoFar.Equals(possiblyLessSpecificCandidate, TypeCompareKind.AllIgnoreOptions))
{
// When the types are equivalent, merge them.
return bestSoFar.MergeEquivalentTypes(possiblyLessSpecificCandidate, VarianceKind.Out);
}
else if (Conversions.HasImplicitReferenceConversion(bestSoFar, possiblyLessSpecificCandidate, ref useSiteDiagnostics))
{
// When there is an implicit reference conversion from T to U, U is less specific
return possiblyLessSpecificCandidate;
}
else if (Conversions.HasBoxingConversion(bestSoFar, possiblyLessSpecificCandidate, ref useSiteDiagnostics))
{
// when there is a boxing conversion from T to U, U is less specific.
return possiblyLessSpecificCandidate;
}
else
{
// We have no improved candidate to offer.
return null;
}
}
}
else
{
......@@ -1347,20 +1422,6 @@ void addSubpatternsForTuple(ImmutableArray<TypeWithAnnotations> elementTypes)
var right = BindPattern(node.RightPattern, left.ConvertedType, leftOutputValEscape, permitDesignations, hasErrors, diagnostics);
return new BoundBinaryPattern(node, disjunction: isDisjunction, left, right, inputType: inputType, convertedType: right.ConvertedType, hasErrors);
}
TypeSymbol commonType(SyntaxNode node, TypeSymbol t1, TypeSymbol t2, DiagnosticBag diagnostics)
{
if (t1.Equals(t2, TypeCompareKind.AllIgnoreOptions))
return t2;
HashSet<DiagnosticInfo>? useSiteDiagnostics = null;
TypeSymbol result =
(ExpressionOfTypeMatchesPatternType(Conversions, expressionType: t1, patternType: t2, ref useSiteDiagnostics, out _) == true) ? t2 :
(ExpressionOfTypeMatchesPatternType(Conversions, expressionType: t2, patternType: t1, ref useSiteDiagnostics, out _) == true) ? t1 :
inputType;
diagnostics.Add(node.Location, useSiteDiagnostics);
return result;
}
}
}
}
......@@ -2308,7 +2308,7 @@ private bool HasImplicitReferenceConversion(TypeWithAnnotations source, TypeWith
return HasImplicitReferenceConversion(source.Type, destination.Type, ref useSiteDiagnostics);
}
private bool HasImplicitReferenceConversion(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
internal bool HasImplicitReferenceConversion(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
......
......@@ -3241,6 +3241,94 @@ class Q: System.Runtime.CompilerServices.ITuple
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact, WorkItem(43377, "https://github.com/dotnet/roslyn/issues/43377")]
public void OutputType_02()
{
var source = @"#nullable enable
using System;
using System.Collections.Generic;
class C
{
static void Main()
{
// The narrowed type of an or pattern combines the narrowed type of all subpatterns
// beneath all of the or combinators.
// Identity conversions
object o = new Dictionary<object, object>();
{ if (o is (Dictionary<object, dynamic> or Dictionary<dynamic?, object?>) and var i) M(i); } // System.Collections.Generic.Dictionary<object, object>
// Boxing conversions
o = 1;
{ if (o is (long or IComparable) and var i) M(i); } // System.IComparable
{ if (o is (IComparable or long) and var i) M(i); } // System.IComparable
// Incomparable types
o = 1L;
{ if (o is (IEquatable<string> or long) and var i) M(i); } // System.Object
{ if (o is (long or IEquatable<string>) and var i) M(i); } // System.Object
o = new Derived1();
{ if (o is (Derived1 or Derived2) and var i) M(i); } // System.Object
// Implicit reference conversions
o = new Derived1();
{ if (o is (Derived1 or Base) and var i) M(i); } // Base
{ if (o is (Base or Derived1) and var i) M(i); } // Base
// Implicit reference conversions involving variance
o = new X();
{ if (o is (IIn<Derived1> or IIn<Base>) and var i) M(i); } // IIn<Derived1>
{ if (o is (IIn<Base> or IIn<Derived1>) and var i) M(i); } // IIn<Derived1>
{ if (o is (IOut<Derived1> or IOut<Base>) and var i) M(i); } // IOut<Base>
{ if (o is (IOut<Base> or IOut<Derived1>) and var i) M(i); } // IOut<Base>
// Multiple layers of or patterns
o = new Derived1();
{ if (o is (Derived1 or Derived2 or Base) and var i) M(i); } // Base
{ if (o is ((Derived1 or Derived2) or Base) and var i) M(i); } // Base
{ if (o is (Base or (Derived1 or Derived2)) and var i) M(i); } // Base
{ if (o is (Derived1 or Base or Derived2) and var i) M(i); } // Base
{ if (o is ((Derived1 or Derived2) or (Derived1 or Derived2) or Base or (Derived1 or Derived2)) and var i) M(i); } // Base
}
static void M<T>(T t)
{
Console.WriteLine(typeof(T));
}
}
class Base { }
class Derived1 : Base { }
class Derived2 : Base { }
interface IIn<in T> { }
interface IOut<out T> { }
class X : IIn<Base>, IOut<Base> { }
";
var expectedOutput =
@"System.Collections.Generic.Dictionary`2[System.Object,System.Object]
System.IComparable
System.IComparable
System.Object
System.Object
System.Object
Base
Base
IIn`1[Derived1]
IIn`1[Derived1]
IOut`1[Base]
IOut`1[Base]
Base
Base
Base
Base
Base
";
var compilation = CreateCompilation(source + _iTupleSource, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Preview));
compilation.VerifyDiagnostics(
);
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact]
public void DoNotShareTempMutatedThroughReceiver_01()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册