提交 59f8f9d9 编写于 作者: J Julien 提交者: GitHub

Dominant type for tuples (#12834)

上级 544c234f
......@@ -62,10 +62,11 @@ internal partial class Binder
if (source.Kind == BoundKind.TupleLiteral)
{
var sourceTuple = (BoundTupleLiteral)source;
TupleTypeSymbol.ReportNamesMismatchesIfAny(destination.TupleElementNames, sourceTuple, diagnostics);
source = new BoundConvertedTupleLiteral(
sourceTuple.Syntax,
sourceTuple.Type,
sourceTuple.Arguments,
sourceTuple.Type,
sourceTuple.Arguments,
sourceTuple.Type, // same type to keep original element names
sourceTuple.HasErrors);
}
......@@ -90,7 +91,7 @@ internal partial class Binder
return CreateAnonymousFunctionConversion(syntax, source, conversion, isCast, destination, diagnostics);
}
if (conversion.IsTupleLiteralConversion ||
if (conversion.IsTupleLiteralConversion ||
(conversion.Kind == ConversionKind.ImplicitNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion))
{
return CreateTupleLiteralConversion(syntax, (BoundTupleLiteral)source, conversion, isCast, destination, diagnostics);
......@@ -343,7 +344,7 @@ private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, Bo
if (conversion.Kind == ConversionKind.ImplicitNullable)
{
destinationWithoutNullable = destination.GetNullableUnderlyingType();
conversionWithoutNullable = conversion.UnderlyingConversions[0];
conversionWithoutNullable = conversion.UnderlyingConversions[0];
}
Debug.Assert(conversionWithoutNullable.IsTupleLiteralConversion);
......@@ -354,6 +355,8 @@ private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, Bo
var destTupleType = (TupleTypeSymbol)targetType;
// do not lose the original element names in the literal if different from names in the target
TupleTypeSymbol.ReportNamesMismatchesIfAny(targetType.TupleElementNames, sourceTuple, diagnostics);
// Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013)
targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt);
}
......@@ -377,7 +380,7 @@ private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, Bo
BoundExpression result = new BoundConvertedTupleLiteral(
sourceTuple.Syntax,
sourceTuple.Type,
convertedArguments.ToImmutableAndFree(),
convertedArguments.ToImmutableAndFree(),
targetType);
if (sourceTuple.Type != destination)
......
......@@ -8,13 +8,14 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed class BestTypeInferrer
{
private readonly ConversionsBase _conversions;
private BestTypeInferrer(ConversionsBase conversions)
private readonly Conversions _conversions;
private BestTypeInferrer(Conversions conversions)
{
_conversions = conversions;
}
public static TypeSymbol InferBestType(ImmutableArray<TypeSymbol> types, ConversionsBase conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public static TypeSymbol InferBestType(ImmutableArray<TypeSymbol> types, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
var inferrer = new BestTypeInferrer(conversions);
return inferrer.GetBestType(types, ref useSiteDiagnostics);
......@@ -24,7 +25,7 @@ public static TypeSymbol InferBestType(ImmutableArray<TypeSymbol> types, Convers
/// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
/// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
/// </remarks>
public static TypeSymbol InferBestType(ImmutableArray<BoundExpression> exprs, ConversionsBase conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public static TypeSymbol InferBestType(ImmutableArray<BoundExpression> exprs, Conversions conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC: 7.5.2.14 Finding the best common type of a set of expressions
// SPEC: In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and
......@@ -64,7 +65,7 @@ public static TypeSymbol InferBestType(ImmutableArray<BoundExpression> exprs, Co
/// This method implements best type inference for the conditional operator ?:.
/// NOTE: If either expression is an error type, we return error type as the inference result.
/// </remarks>
public static TypeSymbol InferBestTypeForConditionalOperator(BoundExpression expr1, BoundExpression expr2, ConversionsBase conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public static TypeSymbol InferBestTypeForConditionalOperator(BoundExpression expr1, BoundExpression expr2, Conversions conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC: The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
// SPEC: • If x has type X and y has type Y then
......@@ -135,11 +136,14 @@ private TypeSymbol GetBestType(ImmutableArray<TypeSymbol> types, ref HashSet<Dia
}
TypeSymbol best = null;
foreach (var type in types)
int bestIndex = -1;
for(int i = 0; i < types.Length; i++)
{
TypeSymbol type = types[i];
if ((object)best == null)
{
best = type;
bestIndex = i;
}
else
{
......@@ -149,9 +153,10 @@ private TypeSymbol GetBestType(ImmutableArray<TypeSymbol> types, ref HashSet<Dia
{
best = null;
}
else if ((object)better == (object)type)
else if ((object)better != (object)best)
{
best = type;
best = better;
bestIndex = i;
}
}
}
......@@ -163,14 +168,12 @@ private TypeSymbol GetBestType(ImmutableArray<TypeSymbol> types, ref HashSet<Dia
// We have actually only determined that every type *after* best was worse. Now check
// that every type *before* best was also worse.
foreach (var type in types)
for (int i = 0; i < bestIndex; i++)
{
if ((object)type == (object)best)
{
break;
}
TypeSymbol type = types[i];
TypeSymbol better = Better(best, type, ref useSiteDiagnostics);
if ((object)Better(best, type, ref useSiteDiagnostics) != (object)best)
if (better != best)
{
return null;
}
......@@ -179,6 +182,9 @@ private TypeSymbol GetBestType(ImmutableArray<TypeSymbol> types, ref HashSet<Dia
return best;
}
/// <summary>
/// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names).
/// </summary>
private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// Anything is better than an error sym.
......@@ -207,6 +213,11 @@ private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<Diagno
return type2;
}
if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
return MethodTypeInferrer.MergeTupleNames(type1, type2, MethodTypeInferrer.MergeDynamic(type1, type2, type1, _conversions.CorLibrary), _conversions.CorLibrary);
}
return null;
}
......
......@@ -35,6 +35,8 @@ protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth)
protected abstract Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool forCast);
internal AssemblySymbol CorLibrary { get { return corLibrary; } }
/// <summary>
/// Determines if the source expression is convertible to the destination type via
/// any built-in or user-defined implicit conversion.
......
......@@ -4,11 +4,11 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Collections;
/*
SPEC:
......@@ -210,8 +210,7 @@ private enum Dependency
ImmutableArray<RefKind> formalParameterRefKinds, // Optional; assume all value if missing.
ImmutableArray<BoundExpression> arguments,// Required; in scenarios like method group conversions where there are
// no arguments per se we cons up some fake arguments.
ref HashSet<DiagnosticInfo> useSiteDiagnostics
)
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert(!methodTypeParameters.IsDefault);
Debug.Assert(methodTypeParameters.Length > 0);
......@@ -2464,43 +2463,36 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
}
}
// SPEC: * If among the remaining candidate types there is a unique type V to
// SPEC: which there is an implicit conversion from all the other candidate
// SPEC: types, then the parameter is fixed to V.
TypeSymbol best = null;
foreach (var candidate in candidates)
{
foreach (var candidate2 in candidates)
// SPEC: * If among the remaining candidate types there is a unique type V to
// SPEC: which there is an implicit conversion from all the other candidate
// SPEC: types, then the parameter is fixed to V.
TypeSymbol best = null;
foreach (var candidate in candidates)
{
if (candidate != candidate2 && !ImplicitConversionExists(candidate2, candidate, ref useSiteDiagnostics))
foreach (var candidate2 in candidates)
{
goto OuterBreak;
if (candidate != candidate2 && !ImplicitConversionExists(candidate2, candidate, ref useSiteDiagnostics))
{
goto OuterBreak;
}
}
}
if ((object)best == null)
{
best = candidate;
}
else if (best.Equals(candidate, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
if (candidate.IsTupleType)
if ((object)best == null)
{
best = candidate.TupleUnderlyingType;
break;
best = candidate;
}
// SPEC: 4.7 The Dynamic Type
// Type inference (7.5.2) will prefer dynamic over object if both are candidates.
//
// This rule doesn't have to be implemented explicitly due to special handling of
// conversions from dynamic in ImplicitConversionExists helper.
//
Debug.Assert(!(best.IsObjectType() && candidate.IsDynamic()));
Debug.Assert(!(best.IsDynamic() && candidate.IsObjectType()));
// best candidate is not unique
return false;
else if (best.Equals(candidate, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
// SPEC: 4.7 The Dynamic Type
// Type inference (7.5.2) will prefer dynamic over object if both are candidates.
//
// This rule doesn't have to be implemented explicitly due to special handling of
// conversions from dynamic in ImplicitConversionExists helper.
//
Debug.Assert(!(best.IsObjectType() && candidate.IsDynamic()));
Debug.Assert(!(best.IsDynamic() && candidate.IsObjectType()));
best = MergeTupleNames(best, candidate, MergeDynamic(best, candidate, best, _conversions.CorLibrary), _conversions.CorLibrary);
}
OuterBreak:
......@@ -2523,6 +2515,58 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
}
}
/// <summary>
/// Takes the dynamic flags from both types and applies them onto the target.
/// </summary>
internal static TypeSymbol MergeDynamic(TypeSymbol first, TypeSymbol second, TypeSymbol target, AssemblySymbol corLibrary)
{
// SPEC: 4.7 The Dynamic Type
// Type inference (7.5.2) will prefer dynamic over object if both are candidates.
if (first.Equals(second, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreTupleNames))
{
return target;
}
ImmutableArray<bool> flags1 = CSharpCompilation.DynamicTransformsEncoder.EncodeWithoutCustomModifierFlags(first, RefKind.None);
ImmutableArray<bool> flags2 = CSharpCompilation.DynamicTransformsEncoder.EncodeWithoutCustomModifierFlags(second, RefKind.None);
ImmutableArray<bool> mergedFlags = flags1.ZipAsArray(flags2, (f1, f2) => f1 | f2);
return DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(target, corLibrary, RefKind.None, mergedFlags);
}
/// <summary>
/// Takes the names from the two types, finds the common names, and applies them onto the target.
/// </summary>
internal static TypeSymbol MergeTupleNames(TypeSymbol first, TypeSymbol second, TypeSymbol target, AssemblySymbol corLibrary)
{
if (!target.ContainsTuple() ||
first.Equals(second, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreDynamic) ||
!target.ContainsTupleNames())
{
return target;
}
ImmutableArray<string> names1 = CSharpCompilation.TupleNamesEncoder.Encode(first);
ImmutableArray<string> names2 = CSharpCompilation.TupleNamesEncoder.Encode(second);
ImmutableArray<string> mergedNames;
if (names1.IsDefault || names2.IsDefault)
{
mergedNames = default(ImmutableArray<string>);
}
else
{
Debug.Assert(names1.Length == names2.Length);
mergedNames = names1.ZipAsArray(names2, (n1, n2) => string.CompareOrdinal(n1, n2) == 0 ? n1 : null);
if (mergedNames.All(n => n == null))
{
mergedNames = default(ImmutableArray<string>);
}
}
return TupleTypeDecoder.DecodeTupleTypesIfApplicable(target, corLibrary, mergedNames);
}
private bool ImplicitConversionExists(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC VIOLATION: For the purpose of algorithm in Fix method, dynamic type is not considered convertible to any other type, including object.
......
......@@ -2721,7 +2721,12 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
{
// infer generic type arguments:
MemberAnalysisResult inferenceError;
typeArguments = InferMethodTypeArguments(method, leastOverriddenMethod.ConstructedFrom.TypeParameters, arguments, originalEffectiveParameters, out inferenceError, ref useSiteDiagnostics);
typeArguments = InferMethodTypeArguments(method,
leastOverriddenMethod.ConstructedFrom.TypeParameters,
arguments,
originalEffectiveParameters,
out inferenceError,
ref useSiteDiagnostics);
if (typeArguments.IsDefault)
{
return new MemberResolutionResult<TMember>(member, leastOverriddenMember, inferenceError);
......
......@@ -13363,6 +13363,24 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to The tuple element name &apos;{0}&apos; is ignored because a different name is specified by the assignment target..
/// </summary>
internal static string WRN_TupleLiteralNameMismatch {
get {
return ResourceManager.GetString("WRN_TupleLiteralNameMismatch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The tuple element name is ignored because a different name is specified by the assignment target..
/// </summary>
internal static string WRN_TupleLiteralNameMismatch_Title {
get {
return ResourceManager.GetString("WRN_TupleLiteralNameMismatch_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Type parameter &apos;{0}&apos; has the same name as the type parameter from outer type &apos;{1}&apos;.
/// </summary>
......
......@@ -4921,6 +4921,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="TypeMustBeVar" xml:space="preserve">
<value>The type must be 'var'.</value>
</data>
<data name="WRN_TupleLiteralNameMismatch" xml:space="preserve">
<value>The tuple element name '{0}' is ignored because a different name is specified by the assignment target.</value>
</data>
<data name="WRN_TupleLiteralNameMismatch_Title" xml:space="preserve">
<value>The tuple element name is ignored because a different name is specified by the assignment target.</value>
</data>
<data name="ERR_DeconstructionVarFormDisallowsSpecificType" xml:space="preserve">
<value>Deconstruction `var (...)` form disallows a specific type for 'var'.</value>
</data>
......
......@@ -2826,7 +2826,7 @@ protected override INamedTypeSymbol CommonCreateTupleTypeSymbol(INamedTypeSymbol
memberTypes[i].EnsureCSharpSymbolOrNull<ITypeSymbol, TypeSymbol>($"{nameof(memberTypes)}[{i}]");
}
var fields = memberTypes.SelectAsArray((type, index, loc) => new AnonymousTypeField(memberNames[index], loc, (TypeSymbol)type), Location.None);
var fields = memberTypes.ZipAsArray(memberNames, (type, name) => new AnonymousTypeField(name, Location.None, (TypeSymbol)type));
var descriptor = new AnonymousTypeDescriptor(fields, Location.None);
return this.AnonymousTypeManager.ConstructAnonymousTypeSymbol(descriptor);
......
......@@ -1354,9 +1354,10 @@ internal enum ErrorCode
ERR_PatternWrongType = 8121,
#endregion diagnostics for pattern-matching introduced in C# 7
// Available = 8122, 8123
// Available = 8122
#region tuple diagnostics introduced in C# 7
WRN_TupleLiteralNameMismatch = 8123,
ERR_TupleTooFewElements = 8124,
ERR_TupleReservedMemberName = 8125,
ERR_TupleReservedMemberNameAnyPosition = 8126,
......@@ -1368,17 +1369,14 @@ internal enum ErrorCode
ERR_DeconstructWrongCardinality = 8132,
ERR_CannotDeconstructDynamic = 8133,
ERR_DeconstructTooFewElements = 8134,
ERR_ConversionNotTupleCompatible = 8135,
ERR_DeconstructionVarFormDisallowsSpecificType = 8136,
ERR_TupleElementNamesAttributeMissing = 8137,
ERR_ExplicitTupleElementNames = 8138,
ERR_CantChangeTupleNamesOnOverride = 8139,
ERR_DuplicateInterfaceWithTupleNamesInBaseList = 8140,
ERR_ImplBadTupleNames = 8141,
ERR_PartialMethodInconsistentTupleNames = 8142,
#endregion tuple diagnostics introduced in C# 7
// Available = 8143, 8144
......
......@@ -316,6 +316,7 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_ReferencedAssemblyDoesNotHaveStrongName:
case ErrorCode.WRN_AlignmentMagnitude:
case ErrorCode.WRN_AttributeIgnoredWhenPublicSigning:
case ErrorCode.WRN_TupleLiteralNameMismatch:
return 1;
default:
return 0;
......
......@@ -85,7 +85,6 @@ private DynamicTypeDecoder(ImmutableArray<bool> dynamicTransformFlags, bool have
ImmutableArray<bool> dynamicTransformFlags,
bool checkLength = true)
{
Debug.Assert(containingAssembly is SourceAssemblySymbol); // Doesn't happen during decoding.
return TransformTypeInternal(
type,
containingAssembly,
......@@ -138,7 +137,7 @@ private TypeSymbol TransformType(TypeSymbol type)
Debug.Assert(_index >= 0);
if (!HasFlag ||
PeekFlag() && type.SpecialType != SpecialType.System_Object)
PeekFlag() && (type.SpecialType != SpecialType.System_Object && !type.IsDynamic()))
{
// Bail, since flags are invalid.
return null;
......@@ -254,7 +253,7 @@ private NamedTypeSymbol TransformNamedType(NamedTypeSymbol namedType, bool isCon
{
var newTypeArguments = customModifiers.IsDefault ?
transformedTypeArguments.SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers) :
transformedTypeArguments.Zip(customModifiers, (t, m) => new TypeWithModifiers(t, m)).AsImmutable();
transformedTypeArguments.ZipAsArray(customModifiers, (t, m) => new TypeWithModifiers(t, m));
if (containerIsChanged)
{
......
......@@ -197,8 +197,7 @@ private NamedTypeSymbol DecodeNamedType(NamedTypeSymbol type)
if (typeArgsChanged || containerChanged)
{
var newTypeArgs = type.HasTypeArgumentsCustomModifiers
? decodedArgs.Zip(type.TypeArgumentsCustomModifiers,
(t, m) => new TypeWithModifiers(t, m)).AsImmutable()
? decodedArgs.ZipAsArray(type.TypeArgumentsCustomModifiers, (t, m) => new TypeWithModifiers(t, m))
: decodedArgs.SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers);
if (containerChanged)
......
......@@ -376,6 +376,28 @@ internal static void VerifyTupleTypePresent(int cardinality, CSharpSyntaxNode sy
}
}
internal static void ReportNamesMismatchesIfAny(ImmutableArray<string> destinationNames, BoundTupleLiteral literal, DiagnosticBag diagnostics)
{
var sourceNames = literal.ArgumentNamesOpt;
if (sourceNames.IsDefault)
{
return;
}
int sourceLength = sourceNames.Length;
bool allMissing = destinationNames.IsDefault;
Debug.Assert(allMissing || destinationNames.Length == sourceLength);
for (int i = 0; i < sourceLength; i++)
{
var sourceName = sourceNames[i];
if (sourceName != null && (allMissing || string.CompareOrdinal(destinationNames[i], sourceName) != 0))
{
diagnostics.Add(ErrorCode.WRN_TupleLiteralNameMismatch, literal.Arguments[i].Syntax.Location, sourceName);
}
}
}
/// <summary>
/// Find the well-known ValueTuple type of a given arity.
/// For example, for arity=2:
......
......@@ -808,6 +808,12 @@ public static bool ContainsDynamic(this TypeSymbol type)
internal static bool ContainsTuple(this TypeSymbol type) =>
(object)type.VisitType((TypeSymbol t, object _1, bool _2) => t.IsTupleType, null) != null;
/// <summary>
/// Return true if the type contains any tuples with element names.
/// </summary>
internal static bool ContainsTupleNames(this TypeSymbol type) =>
(object)type.VisitType((TypeSymbol t, object _1, bool _2) => t.IsTupleType && !t.TupleElementNames.IsDefault , null) != null;
/// <summary>
/// Guess the non-error type that the given type was intended to represent.
/// If the type itself is not an error type, then it will be returned.
......
......@@ -1137,25 +1137,28 @@ public unsafe class C
static void M()
{
var x = s1 ? d1 : s2;
var y = s1 ? d2 : M;
var z = s1 ? M : d2;
var v = s1 ? ptr : d2;
var w = s1 ? d2 : ptr;
var x = s1 ? d1 : s2; // ok
var y = s1 ? d2 : M;
var z = s1 ? M : d2;
var v = s1 ? ptr : d2;
var w = s1 ? d2 : ptr;
}
}
";
CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
// (11,17): error CS0172: Type of conditional expression cannot be determined because 'dynamic[]' and 'object[]' implicitly convert to one another
Diagnostic(ErrorCode.ERR_AmbigQM, "s1 ? d1 : s2").WithArguments("dynamic[]", "object[]"),
// (12,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'dynamic' and 'method group'
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? d2 : M").WithArguments("dynamic", "method group"),
// (13,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'dynamic'
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? M : d2").WithArguments("method group", "dynamic"),
// (16,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'void*' and 'dynamic'
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? ptr : d2").WithArguments("void*", "dynamic"),
// (13,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'dynamic' and 'method group'
// var y = s1 ? d2 : M;
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? d2 : M").WithArguments("dynamic", "method group").WithLocation(13, 17),
// (14,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'dynamic'
// var z = s1 ? M : d2;
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? M : d2").WithArguments("method group", "dynamic").WithLocation(14, 17),
// (15,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'void*' and 'dynamic'
// var v = s1 ? ptr : d2;
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? ptr : d2").WithArguments("void*", "dynamic").WithLocation(15, 17),
// (16,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'dynamic' and 'void*'
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? d2 : ptr").WithArguments("dynamic", "void*"));
// var w = s1 ? d2 : ptr;
Diagnostic(ErrorCode.ERR_InvalidQM, "s1 ? d2 : ptr").WithArguments("dynamic", "void*").WithLocation(16, 17)
);
}
#endregion
......
......@@ -181,16 +181,8 @@ public void WarningLevel_2()
Assert.True(ErrorFacts.IsWarning(errorCode));
switch (errorCode)
{
case ErrorCode.WRN_MainIgnored:
Assert.Equal(2, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_DelaySignButNoKey:
case ErrorCode.WRN_AttributeIgnoredWhenPublicSigning:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_InvalidVersionFormat:
Assert.Equal(4, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_UnimplementedCommandLineSwitch:
case ErrorCode.WRN_CallerFilePathPreferredOverCallerMemberName:
case ErrorCode.WRN_CallerLineNumberPreferredOverCallerMemberName:
......@@ -198,25 +190,26 @@ public void WarningLevel_2()
case ErrorCode.WRN_AssemblyAttributeFromModuleIsOverridden:
case ErrorCode.WRN_RefCultureMismatch:
case ErrorCode.WRN_ConflictingMachineAssembly:
case ErrorCode.WRN_FilterIsConstant:
case ErrorCode.WRN_AnalyzerCannotBeCreated:
case ErrorCode.WRN_NoAnalyzerInAssembly:
case ErrorCode.WRN_UnableToLoadAnalyzer:
case ErrorCode.WRN_ReferencedAssemblyDoesNotHaveStrongName:
case ErrorCode.WRN_AlignmentMagnitude:
case ErrorCode.WRN_TupleLiteralNameMismatch:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_MainIgnored:
case ErrorCode.WRN_NubExprIsConstBool2:
case ErrorCode.WRN_UnqualifiedNestedTypeInCref:
case ErrorCode.WRN_NoRuntimeMetadataVersion:
Assert.Equal(2, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_FilterIsConstant:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_PdbLocalNameTooLong:
Assert.Equal(3, ErrorFacts.GetWarningLevel(errorCode));
break;
case ErrorCode.WRN_AnalyzerCannotBeCreated:
case ErrorCode.WRN_NoAnalyzerInAssembly:
case ErrorCode.WRN_UnableToLoadAnalyzer:
case ErrorCode.WRN_ReferencedAssemblyDoesNotHaveStrongName:
case ErrorCode.WRN_AlignmentMagnitude:
Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode));
case ErrorCode.WRN_InvalidVersionFormat:
Assert.Equal(4, ErrorFacts.GetWarningLevel(errorCode));
break;
default:
// If a new warning is added, this test will fail
......
......@@ -364,6 +364,38 @@ public void SelectAsArray()
AssertEx.Equal(new[] { 10, 20, 30, 40, 50 }, ImmutableArray.Create(1, 2, 3, 4, 5).SelectAsArray(i => 10 * i));
}
[Fact]
public void ZipAsArray()
{
var empty = ImmutableArray.Create<object>();
Assert.True(empty.SequenceEqual(empty.ZipAsArray(empty, (item1, item2) => item1)));
var single1 = ImmutableArray.Create(1);
var single2 = ImmutableArray.Create(10);
var single3 = ImmutableArray.Create(11);
Assert.True(single3.SequenceEqual(single1.ZipAsArray(single2, (item1, item2) => item1 + item2)));
var pair1 = ImmutableArray.Create(1, 2);
var pair2 = ImmutableArray.Create(10, 11);
var pair3 = ImmutableArray.Create(11, 13);
Assert.True(pair3.SequenceEqual(pair1.ZipAsArray(pair2, (item1, item2) => item1 + item2)));
var triple1 = ImmutableArray.Create(1, 2, 3);
var triple2 = ImmutableArray.Create(10, 11, 12);
var triple3 = ImmutableArray.Create(11, 13, 15);
Assert.True(triple3.SequenceEqual(triple1.ZipAsArray(triple2, (item1, item2) => item1 + item2)));
var quad1 = ImmutableArray.Create(1, 2, 3, 4);
var quad2 = ImmutableArray.Create(10, 11, 12, 13);
var quad3 = ImmutableArray.Create(11, 13, 15, 17);
Assert.True(quad3.SequenceEqual(quad1.ZipAsArray(quad2, (item1, item2) => item1 + item2)));
var quin1 = ImmutableArray.Create(1, 2, 3, 4, 5);
var quin2 = ImmutableArray.Create(10, 11, 12, 13, 14);
var quin3 = ImmutableArray.Create(11, 13, 15, 17, 19);
Assert.True(quin3.SequenceEqual(quin1.ZipAsArray(quin2, (item1, item2) => item1 + item2)));
}
[Fact]
public void WhereAsArray()
{
......
......@@ -185,6 +185,41 @@ public static ImmutableArray<byte> ToImmutable(this MemoryStream stream)
}
}
/// <summary>
/// Zips two immutable arrays together through a mapping function, producing another immutable array.
/// </summary>
/// <returns>If the items's length is 0, this will return an empty immutable array.</returns>
public static ImmutableArray<TResult> ZipAsArray<T1, T2, TResult>(this ImmutableArray<T1> self, ImmutableArray<T2> other, Func<T1, T2, TResult> map)
{
Debug.Assert(self.Length == other.Length);
switch (self.Length)
{
case 0:
return ImmutableArray<TResult>.Empty;
case 1:
return ImmutableArray.Create(map(self[0], other[0]));
case 2:
return ImmutableArray.Create(map(self[0], other[0]), map(self[1], other[1]));
case 3:
return ImmutableArray.Create(map(self[0], other[0]), map(self[1], other[1]), map(self[2], other[2]));
case 4:
return ImmutableArray.Create(map(self[0], other[0]), map(self[1], other[1]), map(self[2], other[2]), map(self[3], other[3]));
default:
var builder = ArrayBuilder<TResult>.GetInstance(self.Length);
for (int i = 0; i < self.Length; i++)
{
builder.Add(map(self[i], other[i]));
}
return builder.ToImmutableAndFree();
}
}
/// <summary>
/// Creates a new immutable array based on filtered elements by the predicate. The array must not be null.
/// </summary>
......
......@@ -2614,8 +2614,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
i = i + 1
Next
Dim fields = memberTypes.SelectAsArray(
Function(type, index, loc) New AnonymousTypeField(memberNames(index), DirectCast(type, TypeSymbol), loc), Location.None)
Dim fields = memberTypes.ZipAsArray(
memberNames,
Function(type, name) New AnonymousTypeField(name, DirectCast(type, TypeSymbol), Location.None))
Dim descriptor = New AnonymousTypeDescriptor(fields, Location.None, isImplicitlyDeclared:=False)
Return Me.AnonymousTypeManager.ConstructAnonymousTypeSymbol(descriptor)
......
......@@ -1137,7 +1137,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
typeArgumentsCustomModifiers = type.TypeArgumentsCustomModifiers.Concat(typeArgumentsCustomModifiers)
End While
Return ImmutableArray.CreateRange(typeArguments.Zip(typeArgumentsCustomModifiers, Function(a, m) New TypeWithModifiers(a, m)))
Return typeArguments.ZipAsArray(typeArgumentsCustomModifiers, Function(a, m) New TypeWithModifiers(a, m))
End Function
''' <summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册