protected internal override object VisitPointerType(PointerTypeSymbol symbol, ArrayBuilder<SymbolDescriptionPart> builder) { VisitType(symbol.PointedAtType, builder); AddPunctuation(SyntaxKind.AsteriskToken, builder); return null; }
internal Context(ArrayBuilder<SyntaxTree> builder, Compilation compilation, string path, bool writeToDisk) { _builder = builder; _compilation = compilation; _path = path; _writeToDisk = writeToDisk; }
protected internal override object VisitArrayType(ArrayTypeSymbol symbol, ArrayBuilder<SymbolDescriptionPart> builder) { //See spec section 12.1 for the order of rank specificiers //e.g. int[][,][,,] is stored as // ArrayType // Rank = 1 // ElementType = ArrayType // Rank = 2 // ElementType = ArrayType // Rank = 3 // ElementType = int TypeSymbol underlyingNonArrayType = symbol.ElementType; while (underlyingNonArrayType.TypeKind == TypeKind.ArrayType) { underlyingNonArrayType = ((ArrayTypeSymbol)underlyingNonArrayType).ElementType; } VisitType(underlyingNonArrayType, builder); ArrayTypeSymbol arrayType = symbol; while (arrayType != null) { AddArrayRank(arrayType, builder); arrayType = arrayType.ElementType as ArrayTypeSymbol; } return null; }
internal static void AddSymbolDisplayParts(ArrayBuilder<SymbolDisplayPart> parts, string str) { PooledStringBuilder pooledBuilder = PooledStringBuilder.GetInstance(); StringBuilder sb = pooledBuilder.Builder; int lastKind = -1; foreach (int token in TokenizeString(str, quote: true, nonPrintableSubstitute: '\0', useHexadecimalNumbers: true)) { int kind = token >> 16; // merge contiguous tokens of the same kind into a single part: if (lastKind >= 0 && lastKind != kind) { parts.Add(new SymbolDisplayPart((SymbolDisplayPartKind)lastKind, null, sb.ToString())); sb.Clear(); } lastKind = kind; sb.Append(unchecked((char)token)); } if (lastKind >= 0) { parts.Add(new SymbolDisplayPart((SymbolDisplayPartKind)lastKind, null, sb.ToString())); } pooledBuilder.Free(); }
/// <summary> /// Lower the given decision tree into the given statement builder. /// </summary> public void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree, ArrayBuilder<BoundStatement> loweredDecisionTree) { var oldLoweredDecisionTree = this._loweredDecisionTree; this._loweredDecisionTree = loweredDecisionTree; LowerDecisionTree(expression, decisionTree); this._loweredDecisionTree = oldLoweredDecisionTree; }
internal override void GetRows( ResultProvider resultProvider, ArrayBuilder<EvalResult> rows, DkmInspectionContext inspectionContext, EvalResultDataItem parent, DkmClrValue value, int startIndex, int count, bool visitAll, ref int index) { var fields = GetFields(); int startIndex2; int count2; GetIntersection(startIndex, count, index, fields.Count, out startIndex2, out count2); int offset = startIndex2 - index; for (int i = 0; i < count2; i++) { var row = GetMemberRow(resultProvider, inspectionContext, value, fields[i + offset], parent); rows.Add(row); } index += fields.Count; }
/// <summary> /// Adds aliases of a specified reference to the merged set of aliases. /// Consider the following special cases: /// /// o {} + {} = {} /// If neither reference has any aliases then the result has no aliases. /// /// o {A} + {} = {A, global} /// {} + {A} = {A, global} /// /// If one and only one of the references has aliases we add the global alias since the /// referenced declarations should now be accessible both via existing aliases /// as well as unqualified. /// /// o {A, A} + {A, B, B} = {A, A, B, B} /// We preserve dups in each alias array, but avoid making more dups when merging. /// </summary> internal void Merge(MetadataReference reference) { if (reference.Properties.HasRecursiveAliases) { if (RecursiveAliasesOpt == null) { RecursiveAliasesOpt = ArrayBuilder<string>.GetInstance(); RecursiveAliasesOpt.AddRange(reference.Properties.Aliases); return; } } else { if (AliasesOpt == null) { AliasesOpt = ArrayBuilder<string>.GetInstance(); AliasesOpt.AddRange(reference.Properties.Aliases); return; } } Merge( aliases: reference.Properties.HasRecursiveAliases ? RecursiveAliasesOpt : AliasesOpt, newAliases: reference.Properties.Aliases); }
private void AccessTupleFields(BoundDeconstructionAssignmentOperator node, BoundDeconstructionDeconstructStep deconstruction, ArrayBuilder<LocalSymbol> temps, ArrayBuilder<BoundExpression> stores, ArrayBuilder<BoundValuePlaceholderBase> placeholders) { var target = PlaceholderReplacement(deconstruction.TargetPlaceholder); var tupleType = target.Type.IsTupleType ? target.Type : TupleTypeSymbol.Create((NamedTypeSymbol)target.Type); var tupleElementTypes = tupleType.TupleElementTypes; var numElements = tupleElementTypes.Length; CSharpSyntaxNode syntax = node.Syntax; // save the target as we need to access it multiple times BoundAssignmentOperator assignmentToTemp; var savedTuple = _factory.StoreToTemp(target, out assignmentToTemp); stores.Add(assignmentToTemp); temps.Add(savedTuple.LocalSymbol); // list the tuple fields accessors var fields = tupleType.TupleElementFields; for (int i = 0; i < numElements; i++) { var field = fields[i]; DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic(); if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error) { Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location); } var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty); AddPlaceholderReplacement(deconstruction.OutputPlaceholders[i], fieldAccess); placeholders.Add(deconstruction.OutputPlaceholders[i]); } }
/// <summary> /// Create a SequencePointList with the raw sequence points from an ArrayBuilder. /// A linked list of instances for each syntax tree is created (almost always of length one). /// </summary> public static SequencePointList Create(ArrayBuilder<RawSequencePoint> seqPointBuilder, ILBuilder builder) { if (seqPointBuilder.Count == 0) { return SequencePointList.s_empty; } SequencePointList first = null, current = null; int totalPoints = seqPointBuilder.Count; int last = 0; for (int i = 1; i <= totalPoints; ++i) { if (i == totalPoints || seqPointBuilder[i].SyntaxTree != seqPointBuilder[i - 1].SyntaxTree) { // Create a new list SequencePointList next = new SequencePointList(seqPointBuilder[i - 1].SyntaxTree, GetSubArray(seqPointBuilder, last, i - last, builder)); last = i; // Link together with any additional. if (current == null) { first = current = next; } else { current._next = next; current = next; } } } return first; }
private static void AddNonIncluded(ArrayBuilder<string> builder, string item) { if (!builder.Contains(item)) { builder.Add(item); } }
private static void GetAllScopes( ISymUnmanagedScope root, ArrayBuilder<ISymUnmanagedScope> allScopes, ArrayBuilder<ISymUnmanagedScope> containingScopes, int offset, bool isScopeEndInclusive) { var stack = ArrayBuilder<ISymUnmanagedScope>.GetInstance(); stack.Push(root); while (stack.Any()) { var scope = stack.Pop(); allScopes.Add(scope); if (offset >= 0 && scope.IsInScope(offset, isScopeEndInclusive)) { containingScopes.Add(scope); } foreach (var nested in scope.GetScopes()) { stack.Push(nested); } } stack.Free(); }
private DynamicFlagsCustomTypeInfo(ArrayBuilder<bool> dynamicFlags, int startIndex) { Debug.Assert(dynamicFlags != null); Debug.Assert(startIndex >= 0); int numFlags = dynamicFlags.Count - startIndex; Debug.Assert(numFlags > 0); int numBytes = (numFlags + 7) / 8; byte[] bytes = new byte[numBytes]; bool seenTrue = false; for (int b = 0; b < numBytes; b++) { for (int i = 0; i < 8; i++) { var f = b * 8 + i; if (f >= numFlags) { Debug.Assert(f == numFlags); goto ALL_FLAGS_READ; } if (dynamicFlags[startIndex + f]) { seenTrue = true; bytes[b] |= (byte)(1 << i); } } } ALL_FLAGS_READ: _bytes = seenTrue ? new ReadOnlyCollection<byte>(bytes) : null; }
/// <summary> /// Applies the conversions. /// Adds any new locals to the temps and any new expressions to be evaluated to the stores. /// </summary> private void ApplyConversions(BoundDeconstructionAssignmentOperator node, ArrayBuilder<LocalSymbol> temps, ArrayBuilder<BoundExpression> stores, ArrayBuilder<BoundValuePlaceholderBase> placeholders) { int numConversions = node.ConversionSteps.Length; var conversionLocals = ArrayBuilder<BoundExpression>.GetInstance(); foreach (var conversionInfo in node.ConversionSteps) { // lower the conversions and assignments to locals var localSymbol = new SynthesizedLocal(_factory.CurrentMethod, conversionInfo.OutputPlaceholder.Type, SynthesizedLocalKind.LoweringTemp); var localBound = new BoundLocal(node.Syntax, localSymbol, null, conversionInfo.OutputPlaceholder.Type) { WasCompilerGenerated = true }; temps.Add(localSymbol); conversionLocals.Add(localBound); AddPlaceholderReplacement(conversionInfo.OutputPlaceholder, localBound); placeholders.Add(conversionInfo.OutputPlaceholder); var conversion = VisitExpression(conversionInfo.Assignment); stores.Add(conversion); } }
internal override void AddSynthesizedAttributes(ref ArrayBuilder<SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(ref attributes); var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.SynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor)); }
internal override void AddSynthesizedAttributes(ModuleCompilationState compilationState, ref ArrayBuilder<SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(compilationState, ref attributes); CSharpCompilation compilation = this.DeclaringCompilation; // do not emit CompilerGenerated attributes for fields inside compiler generated types: if (!_containingType.IsImplicitlyDeclared) { AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); } if (!this.SuppressDynamicAttribute && this.Type.ContainsDynamic() && compilation.HasDynamicEmitAttributes() && compilation.CanEmitBoolean()) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.Type, this.CustomModifiers.Length)); } if (Type.ContainsTuple() && compilation.HasTupleNamesAttributes && compilation.CanEmitSpecialType(SpecialType.System_String)) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttributeOpt(Type)); } }
internal SpillBuilder() { locals = ArrayBuilder<LocalSymbol>.GetInstance(); temps = ArrayBuilder<BoundSpillTemp>.GetInstance(); statements = ArrayBuilder<BoundStatement>.GetInstance(); fields = ArrayBuilder<FieldSymbol>.GetInstance(); }
public LargeTextWriter(Encoding encoding, SourceHashAlgorithm checksumAlgorithm, int length) { _encoding = encoding; _checksumAlgorithm = checksumAlgorithm; _chunks = ArrayBuilder<char[]>.GetInstance(1 + length / LargeText.ChunkSize); _bufferSize = Math.Min(LargeText.ChunkSize, length); }
internal override void AddSynthesizedAttributes(ref ArrayBuilder<SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(ref attributes); var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.SynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); }
internal abstract ReadOnlyCollection<byte> CompileGetLocals( ArrayBuilder<LocalAndMethod> locals, bool argumentsOnly, ImmutableArray<Alias> aliases, DiagnosticBag diagnostics, out string typeName, CompilationTestData testData);
// Rewrite collection initializer add method calls: // 2) new List<int> { 1 }; // ~ private void AddCollectionInitializers(ref ArrayBuilder<BoundExpression> dynamicSiteInitializers, ArrayBuilder<BoundExpression> result, BoundExpression rewrittenReceiver, ImmutableArray<BoundExpression> initializers) { Debug.Assert(rewrittenReceiver != null || _inExpressionLambda); foreach (var initializer in initializers) { // In general bound initializers may contain bad expressions or element initializers. // We don't lower them if they contain errors, so it's safe to assume an element initializer. BoundExpression rewrittenInitializer; if (initializer.Kind == BoundKind.CollectionElementInitializer) { rewrittenInitializer = MakeCollectionInitializer(rewrittenReceiver, (BoundCollectionElementInitializer)initializer); } else { Debug.Assert(!_inExpressionLambda); Debug.Assert(initializer.Kind == BoundKind.DynamicCollectionElementInitializer); rewrittenInitializer = MakeDynamicCollectionInitializer(rewrittenReceiver, (BoundDynamicCollectionElementInitializer)initializer); } // the call to Add may be omitted if (rewrittenInitializer != null) { result.Add(rewrittenInitializer); } } }
internal void CollectLocalsFromDeconstruction( ExpressionSyntax declaration, LocalDeclarationKind kind, ArrayBuilder<LocalSymbol> locals, SyntaxNode deconstructionStatement, Binder enclosingBinderOpt = null) { switch (declaration.Kind()) { case SyntaxKind.TupleExpression: { var tuple = (TupleExpressionSyntax)declaration; foreach (var arg in tuple.Arguments) { CollectLocalsFromDeconstruction(arg.Expression, kind, locals, deconstructionStatement, enclosingBinderOpt); } break; } case SyntaxKind.DeclarationExpression: { var declarationExpression = (DeclarationExpressionSyntax)declaration; CollectLocalsFromDeconstruction( declarationExpression.Designation, declarationExpression.Type, kind, locals, deconstructionStatement, enclosingBinderOpt); break; } case SyntaxKind.IdentifierName: break; default: throw ExceptionUtilities.UnexpectedValue(declaration.Kind()); } }
private LookupResult(ObjectPool<LookupResult> pool) { this.pool = pool; this.kind = LookupResultKind.Empty; this.symbolList = new ArrayBuilder<Symbol>(); this.error = null; }
DispatchMapEntry[] BuildDispatchMap(NodeFactory factory) { ArrayBuilder<DispatchMapEntry> dispatchMapEntries = new ArrayBuilder<DispatchMapEntry>(); for (int i = 0; i < _type.RuntimeInterfaces.Length; i++) { var interfaceType = _type.RuntimeInterfaces[i]; Debug.Assert(interfaceType.IsInterface); List<MethodDesc> virtualSlots; factory.VirtualSlots.TryGetValue(interfaceType, out virtualSlots); if (virtualSlots != null) { for (int j = 0; j < virtualSlots.Count; j++) { MethodDesc declMethod = virtualSlots[j]; var implMethod = VirtualFunctionResolution.ResolveInterfaceMethodToVirtualMethodOnType(declMethod, _type.GetClosestMetadataType()); // Interface methods first implemented by a base type in the hierarchy will return null for the implMethod (runtime interface // dispatch will walk the inheritance chain). if (implMethod != null) { var entry = new DispatchMapEntry(); entry.InterfaceIndex = checked((short)i); entry.InterfaceMethodSlot = checked((short)j); entry.ImplementationMethodSlot = checked((short)VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, implMethod)); dispatchMapEntries.Add(entry); } } } } return dispatchMapEntries.ToArray(); }
internal void CollectLocalsFromDeconstruction( ExpressionSyntax declaration, LocalDeclarationKind kind, ArrayBuilder<LocalSymbol> locals, SyntaxNode deconstructionStatement, Binder enclosingBinderOpt = null) { switch (declaration.Kind()) { case SyntaxKind.TupleExpression: { var tuple = (TupleExpressionSyntax)declaration; foreach (var arg in tuple.Arguments) { CollectLocalsFromDeconstruction(arg.Expression, kind, locals, deconstructionStatement, enclosingBinderOpt); } break; } case SyntaxKind.DeclarationExpression: { var declarationExpression = (DeclarationExpressionSyntax)declaration; CollectLocalsFromDeconstruction( declarationExpression.Designation, declarationExpression.Type, kind, locals, deconstructionStatement, enclosingBinderOpt); break; } case SyntaxKind.IdentifierName: break; default: // In broken code, we can have an arbitrary expression here. Collect its expression variables. ExpressionVariableFinder.FindExpressionVariables(this, locals, declaration); break; } }
/// <summary> /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple. /// /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps. /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do. /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables. /// </summary> private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment( CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder<DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null) { // receiver for first Deconstruct step var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue); boundRHS = FixTupleLiteral(checkedVariables, boundRHS, node, diagnostics); if ((object)boundRHS.Type == null) { // we could still not infer a type for the RHS FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, ImmutableArray<BoundDeconstructionDeconstructStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty, ImmutableArray<BoundDeconstructionConstructionStep>.Empty, GetSpecialType(SpecialType.System_Void, diagnostics, node), hasErrors: true); } var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1); var conversionSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1); var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1); var constructionStepsOpt = isDeclaration ? null : ArrayBuilder<BoundDeconstructionConstructionStep>.GetInstance(1); bool hasErrors = !DeconstructIntoSteps( new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt); TypeSymbol returnType = isDeclaration ? GetSpecialType(SpecialType.System_Void, diagnostics, node) : hasErrors ? CreateErrorType() : constructionStepsOpt.Last().OutputPlaceholder.Type; var deconstructions = deconstructionSteps.ToImmutableAndFree(); var conversions = conversionSteps.ToImmutableAndFree(); var assignments = assignmentSteps.ToImmutableAndFree(); var constructions = isDeclaration ? default(ImmutableArray<BoundDeconstructionConstructionStep>) : constructionStepsOpt.ToImmutableAndFree(); FailRemainingInferences(checkedVariables, diagnostics); return new BoundDeconstructionAssignmentOperator( node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, conversions, assignments, constructions, returnType, hasErrors: hasErrors); }
private LookupResult(ObjectPool<LookupResult> pool) { _pool = pool; _kind = LookupResultKind.Empty; _symbolList = new ArrayBuilder<Symbol>(); _error = null; }
internal sealed override void AddSynthesizedAttributes(ModuleCompilationState compilationState, ref ArrayBuilder<SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(compilationState, ref attributes); var compilation = this.DeclaringCompilation; if (this.IsParams) { AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_ParamArrayAttribute__ctor)); } // Synthesize DecimalConstantAttribute if we don't have an explicit custom attribute already: var defaultValue = this.ExplicitDefaultConstantValue; if (defaultValue != ConstantValue.NotAvailable && defaultValue.SpecialType == SpecialType.System_Decimal && DefaultValueFromAttributes == ConstantValue.NotAvailable) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDecimalConstantAttribute(defaultValue.DecimalValue)); } if (this.Type.ContainsDynamic()) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.Type, this.CustomModifiers.Length, this.RefKind)); } if (Type.ContainsTupleNames()) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(Type)); } }
/// <summary> /// In regular C#, all field initializers are assignments to fields and the assigned expressions /// may not reference instance members. /// </summary> private static void BindRegularCSharpFieldInitializers( CSharpCompilation compilation, ImmutableArray<FieldInitializers> initializers, ArrayBuilder<BoundInitializer> boundInitializers, DiagnosticBag diagnostics, bool generateDebugInfo, out ConsList<Imports> firstDebugImports) { firstDebugImports = null; foreach (FieldInitializers siblingInitializers in initializers) { var infos = ArrayBuilder<FieldInitializerInfo>.GetInstance(); // Exact size is not known up front. var locals = GetFieldInitializerInfos(compilation, siblingInitializers, infos, generateDebugInfo, ref firstDebugImports); ArrayBuilder<BoundInitializer> initializersBuilder = locals.IsDefaultOrEmpty ? boundInitializers : ArrayBuilder<BoundInitializer>.GetInstance(infos.Count); foreach (var info in infos) { BoundFieldInitializer boundInitializer = BindFieldInitializer(info.Binder, info.Initializer.Field, info.EqualsValue, diagnostics); initializersBuilder.Add(boundInitializer); } Debug.Assert(locals.IsDefaultOrEmpty == (initializersBuilder == boundInitializers)); if (!locals.IsDefaultOrEmpty) { boundInitializers.Add(new BoundInitializationScope((CSharpSyntaxNode)siblingInitializers.TypeDeclarationSyntax.GetSyntax(), locals, initializersBuilder.ToImmutableAndFree())); } infos.Free(); } }
public SourceAssemblySymbol( PhpCompilation compilation, string assemblySimpleName, string moduleName) { Debug.Assert(compilation != null); Debug.Assert(!String.IsNullOrWhiteSpace(assemblySimpleName)); Debug.Assert(!String.IsNullOrWhiteSpace(moduleName)); _compilation = compilation; _simpleName = assemblySimpleName; var moduleBuilder = new ArrayBuilder<ModuleSymbol>(1); moduleBuilder.Add(new SourceModuleSymbol(this, compilation.SourceSymbolTables, moduleName)); //var importOptions = (compilation.Options.MetadataImportOptions == MetadataImportOptions.All) ? // MetadataImportOptions.All : MetadataImportOptions.Internal; //foreach (PEModule netModule in netModules) //{ // moduleBuilder.Add(new PEModuleSymbol(this, netModule, importOptions, moduleBuilder.Count)); // // SetReferences will be called later by the ReferenceManager (in CreateSourceAssemblyFullBind for // // a fresh manager, in CreateSourceAssemblyReuseData for a reused one). //} _modules = moduleBuilder.ToImmutableAndFree(); }
// Virtual function related functionality public override MethodImplRecord[] FindMethodsImplWithMatchingDeclName(string declName) { MetadataReader metadataReader = _module.MetadataReader; var stringComparer = metadataReader.StringComparer; ArrayBuilder<MethodImplRecord> foundRecords = new ArrayBuilder<MethodImplRecord>(); foreach (var methodImplHandle in _typeDefinition.GetMethodImplementations()) { MethodImplementation methodImpl = metadataReader.GetMethodImplementation(methodImplHandle); EntityHandle methodDeclCheckHandle = methodImpl.MethodDeclaration; HandleKind methodDeclHandleKind = methodDeclCheckHandle.Kind; // We want to check that the method name matches before actually getting the MethodDesc. For MethodSpecifications // we need to dereference that handle to the underlying member reference to look at name matching. if (methodDeclHandleKind == HandleKind.MethodSpecification) { methodDeclCheckHandle = metadataReader.GetMethodSpecification((MethodSpecificationHandle)methodDeclCheckHandle).Method; methodDeclHandleKind = methodDeclCheckHandle.Kind; } bool foundRecord = false; switch (methodDeclHandleKind) { case HandleKind.MethodDefinition: if (stringComparer.Equals(metadataReader.GetMethodDefinition((MethodDefinitionHandle)methodDeclCheckHandle).Name, declName)) { foundRecord = true; } break; case HandleKind.MemberReference: if (stringComparer.Equals(metadataReader.GetMemberReference((MemberReferenceHandle)methodDeclCheckHandle).Name, declName)) { foundRecord = true; } break; default: Debug.Assert(false, "unexpected methodDeclHandleKind"); break; } if (foundRecord) { MethodImplRecord newRecord = new MethodImplRecord(); newRecord.Decl = (MethodDesc)_module.GetObject(methodImpl.MethodDeclaration); newRecord.Body = (MethodDesc)_module.GetObject(methodImpl.MethodBody); foundRecords.Add(newRecord); } } if (foundRecords.Count != 0) return foundRecords.ToArray(); return null; }
private BoundExpression VisitInitializer(BoundExpression node, out InitializerKind kind) { switch (node.Kind) { case BoundKind.ObjectInitializerExpression: { var oi = (BoundObjectInitializerExpression)node; var builder = ArrayBuilder <BoundExpression> .GetInstance(); foreach (BoundAssignmentOperator a in oi.Initializers) { var sym = ((BoundObjectInitializerMember)a.Left).MemberSymbol; // An error is reported in diagnostics pass when a dynamic object initializer is encountered in an ET: Debug.Assert((object)sym != null); InitializerKind elementKind; var value = VisitInitializer(a.Right, out elementKind); switch (elementKind) { case InitializerKind.CollectionInitializer: { var left = InitializerMemberGetter(sym); builder.Add(ExprFactory("ListBind", left, value)); break; } case InitializerKind.Expression: { var left = InitializerMemberSetter(sym); builder.Add(ExprFactory("Bind", left, value)); break; } case InitializerKind.MemberInitializer: { var left = InitializerMemberGetter(sym); builder.Add(ExprFactory("MemberBind", left, value)); break; } default: throw ExceptionUtilities.UnexpectedValue(elementKind); } } kind = InitializerKind.MemberInitializer; return(_bound.Array(MemberBindingType, builder.ToImmutableAndFree())); } case BoundKind.CollectionInitializerExpression: { var ci = (BoundCollectionInitializerExpression)node; Debug.Assert(ci.Initializers.Length != 0); kind = InitializerKind.CollectionInitializer; var builder = ArrayBuilder <BoundExpression> .GetInstance(); // The method invocation must be a static call. // Dynamic calls are not allowed in ETs, an error is reported in diagnostics pass. foreach (BoundCollectionElementInitializer i in ci.Initializers) { BoundExpression elementInit = ExprFactory("ElementInit", _bound.MethodInfo(i.AddMethod), Expressions(i.Arguments)); builder.Add(elementInit); } return(_bound.Array(ElementInitType, builder.ToImmutableAndFree())); } default: { kind = InitializerKind.Expression; return(Visit(node)); } } }
// Returns true if there were any applicable candidates. private bool CandidateOperators(ArrayBuilder <UnaryOperatorSignature> operators, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { bool anyApplicable = false; foreach (var op in operators) { var conversion = Conversions.ClassifyConversionFromExpression(operand, op.OperandType, ref useSiteDiagnostics); if (conversion.IsImplicit) { anyApplicable = true; results.Add(UnaryOperatorAnalysisResult.Applicable(op, conversion)); } else { results.Add(UnaryOperatorAnalysisResult.Inapplicable(op, conversion)); } } return(anyApplicable); }
private void GetAllBuiltInOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // The spec states that overload resolution is performed upon the infinite set of // operators defined on enumerated types, pointers and delegates. Clearly we cannot // construct the infinite set; we have to pare it down. Previous implementations of C# // implement a much stricter rule; they only add the special operators to the candidate // set if one of the operands is of the relevant type. This means that operands // involving user-defined implicit conversions from class or struct types to enum, // pointer and delegate types do not cause the right candidates to participate in // overload resolution. It also presents numerous problems involving delegate variance // and conversions from lambdas to delegate types. // // It is onerous to require the actually specified behavior. We should change the // specification to match the previous implementation. var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance(); this.Compilation.builtInOperators.GetSimpleBuiltInOperators(kind, operators); GetEnumOperations(kind, operand, operators); var pointerOperator = GetPointerOperation(kind, operand); if (pointerOperator != null) { operators.Add(pointerOperator.Value); } CandidateOperators(operators, operand, results, ref useSiteDiagnostics); operators.Free(); }
// Returns true if there were any applicable candidates. private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(operand != null); if ((object)operand.Type == null) { // If the operand has no type -- because it is a null reference or a lambda or a method group -- // there is no way we can determine what type to search for user-defined operators. return(false); } // Spec 7.3.5 Candidate user-defined operators // SPEC: Given a type T and an operation op(A) ... the set of candidate user-defined // SPEC: operators provided by T for op(A) is determined as follows: // SPEC: If T is a nullable type then T0 is its underlying type; otherwise T0 is T. // SPEC: For all operator declarations in T0 and all lifted forms of such operators, if // SPEC: at least one operator is applicable with respect to A then the set of candidate // SPEC: operators consists of all such applicable operators. Otherwise, if T0 is object // SPEC: then the set of candidate operators is empty. Otherwise, the set of candidate // SPEC: operators is the set provided by the direct base class of T0, or the effective // SPEC: base class of T0 if T0 is a type parameter. TypeSymbol type0 = operand.Type.StrippedType(); // Searching for user-defined operators is expensive; let's take an early out if we can. if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0)) { return(false); } string name = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind); var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance(); bool hadApplicableCandidates = false; NamedTypeSymbol current = type0 as NamedTypeSymbol; if ((object)current == null) { current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } if ((object)current == null && type0.IsTypeParameter()) { current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteDiagnostics); } for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { operators.Clear(); GetUserDefinedUnaryOperatorsFromType(current, kind, name, operators); results.Clear(); if (CandidateOperators(operators, operand, results, ref useSiteDiagnostics)) { hadApplicableCandidates = true; break; } } operators.Free(); return(hadApplicableCandidates); }
private void GetEnumOperations(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorSignature> operators) { Debug.Assert(operand != null); var enumType = operand.Type; if ((object)enumType == null) { return; } enumType = enumType.StrippedType(); if (!enumType.IsValidEnumType()) { return; } var nullableEnum = MakeNullable(enumType); switch (kind) { case UnaryOperatorKind.PostfixIncrement: case UnaryOperatorKind.PostfixDecrement: case UnaryOperatorKind.PrefixIncrement: case UnaryOperatorKind.PrefixDecrement: case UnaryOperatorKind.BitwiseComplement: operators.Add(new UnaryOperatorSignature(kind | UnaryOperatorKind.Enum, enumType, enumType)); operators.Add(new UnaryOperatorSignature(kind | UnaryOperatorKind.Lifted | UnaryOperatorKind.Enum, nullableEnum, nullableEnum)); break; } }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if (!_analysis.CatchContainsAwait(node)) { var origCurrentAwaitCatchFrame = _currentAwaitCatchFrame; _currentAwaitCatchFrame = null; var result = base.VisitCatchBlock(node); _currentAwaitCatchFrame = origCurrentAwaitCatchFrame; return(result); } var currentAwaitCatchFrame = _currentAwaitCatchFrame; if (currentAwaitCatchFrame == null) { Debug.Assert(node.Syntax.IsKind(SyntaxKind.CatchClause)); var tryStatementSyntax = (TryStatementSyntax)node.Syntax.Parent; currentAwaitCatchFrame = _currentAwaitCatchFrame = new AwaitCatchFrame(_F, tryStatementSyntax); } var catchType = node.ExceptionTypeOpt ?? _F.SpecialType(SpecialType.System_Object); var catchTemp = _F.SynthesizedLocal(catchType); var storePending = _F.AssignmentExpression( _F.Local(currentAwaitCatchFrame.pendingCaughtException), _F.Convert(currentAwaitCatchFrame.pendingCaughtException.Type, _F.Local(catchTemp))); var setPendingCatchNum = _F.Assignment( _F.Local(currentAwaitCatchFrame.pendingCatch), _F.Literal(currentAwaitCatchFrame.handlers.Count + 1)); // catch (ExType exTemp) // { // pendingCaughtException = exTemp; // catchNo = X; // } BoundCatchBlock catchAndPend; ImmutableArray <LocalSymbol> handlerLocals; var filterPrologueOpt = node.ExceptionFilterPrologueOpt; var filterOpt = node.ExceptionFilterOpt; if (filterOpt == null) { Debug.Assert(filterPrologueOpt is null); // store pending exception // as the first statement in a catch catchAndPend = node.Update( ImmutableArray.Create(catchTemp), _F.Local(catchTemp), catchType, exceptionFilterPrologueOpt: filterPrologueOpt, exceptionFilterOpt: null, body: _F.Block( _F.HiddenSequencePoint(), _F.ExpressionStatement(storePending), setPendingCatchNum), isSynthesizedAsyncCatchAll: node.IsSynthesizedAsyncCatchAll); // catch locals live on the synthetic catch handler block handlerLocals = node.Locals; } else { handlerLocals = ImmutableArray <LocalSymbol> .Empty; // catch locals move up into hoisted locals // since we might need to access them from both the filter and the catch foreach (var local in node.Locals) { currentAwaitCatchFrame.HoistLocal(local, _F); } // store pending exception // as the first expression in a filter var sourceOpt = node.ExceptionSourceOpt; var rewrittenPrologue = (BoundStatementList)this.Visit(filterPrologueOpt); var rewrittenFilter = (BoundExpression)this.Visit(filterOpt); var newFilter = sourceOpt == null? _F.MakeSequence( storePending, rewrittenFilter) : _F.MakeSequence( storePending, AssignCatchSource((BoundExpression)this.Visit(sourceOpt), currentAwaitCatchFrame), rewrittenFilter); catchAndPend = node.Update( ImmutableArray.Create(catchTemp), _F.Local(catchTemp), catchType, exceptionFilterPrologueOpt: rewrittenPrologue, exceptionFilterOpt: newFilter, body: _F.Block( _F.HiddenSequencePoint(), setPendingCatchNum), isSynthesizedAsyncCatchAll: node.IsSynthesizedAsyncCatchAll); } var handlerStatements = ArrayBuilder <BoundStatement> .GetInstance(); handlerStatements.Add(_F.HiddenSequencePoint()); if (filterOpt == null) { var sourceOpt = node.ExceptionSourceOpt; if (sourceOpt != null) { BoundExpression assignSource = AssignCatchSource((BoundExpression)this.Visit(sourceOpt), currentAwaitCatchFrame); handlerStatements.Add(_F.ExpressionStatement(assignSource)); } } handlerStatements.Add((BoundStatement)this.Visit(node.Body)); var handler = _F.Block( handlerLocals, handlerStatements.ToImmutableAndFree() ); currentAwaitCatchFrame.handlers.Add(handler); return(catchAndPend); }
/// <summary> /// Populates a list of unary operator results with those from any /// witness in scope at the operator site. /// </summary> /// <param name="kind"> /// The unary operator kind of the expression. /// </param> /// <param name="operand"> /// The operand expression. /// </param> /// <param name="results"> /// The results list to populate. /// </param> /// <param name="useSiteDiagnostics"> /// The set of diagnostics to populate with any errors. /// </param> /// <returns> /// True if we managed to find candidate operators from the concept /// witnesses in scope; false otherwise. /// </returns> private bool GetUnaryWitnessOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(operand != null); string name = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind); var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance(); foreach (var method in GetWitnessOperators(name, 1, ref useSiteDiagnostics)) { // TODO: nullability operators.Add(new UnaryOperatorSignature(UnaryOperatorKind.UserDefined | kind, method.ParameterTypes[0], method.ReturnType, method)); } bool hasCandidates = CandidateOperators(operators, operand, results, ref useSiteDiagnostics); operators.Free(); return(hasCandidates); }
internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder <SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); AddSynthesizedAttribute(ref attributes, this.DeclaringCompilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor)); }
public override BoundNode VisitTryStatement(BoundTryStatement node) { // if node contains no yields, do regular rewrite in the current frame. if (!ContainsYields(node)) { _tryNestingLevel++; var result = node.Update( (BoundBlock)Visit(node.TryBlock), VisitList(node.CatchBlocks), (BoundBlock)Visit(node.FinallyBlockOpt), node.PreferFaultHandler); _tryNestingLevel--; return(result); } Debug.Assert(node.CatchBlocks.IsEmpty, "try with yields must have no catches"); Debug.Assert(node.FinallyBlockOpt != null, "try with yields must have finally"); // rewrite TryBlock in a new frame. var frame = PushFrame(node); _tryNestingLevel++; var rewrittenBody = (BoundStatement)this.Visit(node.TryBlock); Debug.Assert(!frame.IsRoot()); Debug.Assert(frame.parent.knownStates.ContainsValue(frame), "parent must be aware about states in the child frame"); var finallyMethod = frame.handler; var origMethod = F.CurrentFunction; // rewrite finally block into a Finally method. F.CurrentFunction = finallyMethod; var rewrittenHandler = (BoundStatement)this.Visit(node.FinallyBlockOpt); _tryNestingLevel--; PopFrame(); // { // this.state = parentFinalizeState; // body; // return; // } Debug.Assert(frame.parent.finalizeState == _currentFinallyFrame.finalizeState); rewrittenHandler = F.Block((object)this.cachedThis != null ? ImmutableArray.Create(this.cachedThis) : ImmutableArray <LocalSymbol> .Empty, F.Assignment(F.Field(F.This(), stateField), F.Literal(frame.parent.finalizeState)), CacheThisIfNeeded(), rewrittenHandler, F.Return() ); F.CloseMethod(rewrittenHandler); F.CurrentFunction = origMethod; var bodyStatements = ArrayBuilder <BoundStatement> .GetInstance(); // add a call to the handler after the try body. // // { // this.state = finalizeState; // body; // this.Finally(); // will reset the state to the finally state of the parent. // } bodyStatements.Add(F.Assignment(F.Field(F.This(), stateField), F.Literal(frame.finalizeState))); bodyStatements.Add(rewrittenBody); bodyStatements.Add(F.ExpressionStatement(F.Call(F.This(), finallyMethod))); // handle proxy labels if have any if (frame.proxyLabels != null) { var dropThrough = F.GenerateLabel("dropThrough"); bodyStatements.Add(F.Goto(dropThrough)); var parent = frame.parent; foreach (var p in frame.proxyLabels) { var proxy = p.Value; var destination = p.Key; // branch lands here bodyStatements.Add(F.Label(proxy)); // finalize current state, proceed to destination. bodyStatements.Add(F.ExpressionStatement(F.Call(F.This(), finallyMethod))); // let the parent forward the branch appropriately var parentProxy = parent.ProxyLabelIfNeeded(destination); bodyStatements.Add(F.Goto(parentProxy)); } bodyStatements.Add(F.Label(dropThrough)); } return(F.Block(bodyStatements.ToImmutableAndFree())); }
internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder <SynthesizedAttributeData>?attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); var compilation = this.DeclaringCompilation; var type = this.TypeWithAnnotations; if (type.Type.ContainsDynamic()) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length)); } if (type.Type.ContainsTupleNames()) { AddSynthesizedAttribute(ref attributes, DeclaringCompilation.SynthesizeTupleNamesAttribute(type.Type)); } if (compilation.ShouldEmitNullableAttributes(this)) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, containingType.GetNullableContextValue(), type)); } }
internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder <SynthesizedAttributeData> attributes) { // Emit [Dynamic] on synthesized parameter symbols when the original parameter was dynamic // in order to facilitate debugging. In the case the necessary attributes are missing // this is a no-op. Emitting an error here, or when the original parameter was bound, would // adversely effect the compilation or potentially change overload resolution. var compilation = this.DeclaringCompilation; var type = this.TypeWithAnnotations; if (type.Type.ContainsDynamic() && compilation.HasDynamicEmitAttributes(BindingDiagnosticBag.Discarded, Location.None) && compilation.CanEmitBoolean()) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(type.Type, type.CustomModifiers.Length + this.RefCustomModifiers.Length, this.RefKind)); } if (type.Type.ContainsNativeInteger()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNativeIntegerAttribute(this, type.Type)); } if (type.Type.ContainsTupleNames() && compilation.HasTupleNamesAttributes(BindingDiagnosticBag.Discarded, Location.None) && compilation.CanEmitSpecialType(SpecialType.System_String)) { AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(type.Type)); } if (compilation.ShouldEmitNullableAttributes(this)) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, GetNullableContextValue(), type)); } if (this.RefKind == RefKind.RefReadOnly) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } }
private BoundExpression HoistExpression( BoundExpression expr, AwaitExpressionSyntax awaitSyntaxOpt, int syntaxOffset, RefKind refKind, ArrayBuilder <BoundExpression> sideEffects, ArrayBuilder <StateMachineFieldSymbol> hoistedFields, ref bool needsSacrificialEvaluation) { switch (expr.Kind) { case BoundKind.ArrayAccess: { var array = (BoundArrayAccess)expr; BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); var indices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var index in array.Indices) { indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); } needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions return(array.Update(expression, indices.ToImmutableAndFree(), array.Type)); } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)expr; if (field.FieldSymbol.IsStatic) { // the address of a static field, and the value of a readonly static field, is stable if (refKind != RefKind.None || field.FieldSymbol.IsLet) { return(expr); } goto default; } if (refKind == RefKind.None) { goto default; } var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, isFieldOfStruct ? refKind : RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) { needsSacrificialEvaluation = true; // need the null check in field receiver } return(F.Field(receiver, field.FieldSymbol)); } case BoundKind.ThisReference: case BoundKind.BaseReference: case BoundKind.DefaultExpression: return(expr); case BoundKind.Call: var call = (BoundCall)expr; // NOTE: There are two kinds of 'In' arguments that we may see at this point: // - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier) // - `RefKind.In` (specified with no modifiers and matched an 'In' parameter) // // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. // The "strict" ones do not permit implicit copying, so the same situation should result in an error. if (refKind != RefKind.None && refKind != RefKind.In) { Debug.Assert(call.Method.RefKind != RefKind.None); F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method); } // method call is not referentially transparent, we can only spill the result value. refKind = RefKind.None; goto default; case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; // NOTE: There are two kinds of 'In' arguments that we may see at this point: // - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier) // - `RefKind.In` (specified with no modifiers and matched an 'In' parameter) // // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. // The "strict" ones do not permit implicit copying, so the same situation should result in an error. if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) { Debug.Assert(conditional.IsRef); F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location); } // conditional expr is not referentially transparent, we can only spill the result value. refKind = RefKind.None; goto default; default: if (expr.ConstantValue != null) { return(expr); } if (refKind != RefKind.None) { throw ExceptionUtilities.UnexpectedValue(expr.Kind); } TypeSymbol fieldType = expr.Type; StateMachineFieldSymbol hoistedField; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; Debug.Assert(awaitSyntaxOpt != null); int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); var id = new LocalDebugId(syntaxOffset, ordinal); // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. // However, it may happen that the type changes, in which case we need to allocate a new slot. int slotIndex; if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousHoistedLocalSlotIndex( awaitSyntaxOpt, F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics), kind, id, Diagnostics, out slotIndex)) { slotIndex = _nextFreeHoistedLocalSlot++; } string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); } else { hoistedField = GetOrAllocateReusableHoistedField(fieldType, reused: out _); } hoistedFields.Add(hoistedField); var replacement = F.Field(F.This(), hoistedField); sideEffects.Add(F.AssignmentExpression(replacement, expr)); return(replacement); } }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundStatement PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var hoistedLocalsWithDebugScopes = ArrayBuilder <StateMachineFieldSymbol> .GetInstance(); foreach (var local in locals) { if (!NeedsProxy(local)) { continue; } // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator. if (local.RefKind != RefKind.None) { Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill); continue; } Debug.Assert(local.SynthesizedKind.IsLongLived()); CapturedSymbolReplacement proxy; bool reused = false; if (!proxies.TryGetValue(local, out proxy)) { proxy = new CapturedToStateMachineFieldReplacement(GetOrAllocateReusableHoistedField(TypeMap.SubstituteType(local.Type.TypeSymbol).TypeSymbol, out reused, local), isReusable: true); proxies.Add(local, proxy); } // We need to produce hoisted local scope debug information for user locals as well as // lambda display classes, since Dev12 EE uses them to determine which variables are displayed // in Locals window. if ((local.SynthesizedKind == SynthesizedLocalKind.UserDefined && local.ScopeDesignatorOpt?.Kind() != SyntaxKind.SwitchSection) || local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass) { // NB: This is the case when the local backed by recycled field will not be visible in debugger. // It may be possible in the future, but for now a backing field can be mapped only to a single local. if (!reused) { hoistedLocalsWithDebugScopes.Add(((CapturedToStateMachineFieldReplacement)proxy).HoistedField); } } } var translatedStatement = wrapped(); var variableCleanup = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); // produce cleanup code for all fields of locals defined by this block // as well as all proxies allocated by VisitAssignmentOperator within this block: foreach (var local in locals) { CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { continue; } var simpleProxy = proxy as CapturedToStateMachineFieldReplacement; if (simpleProxy != null) { AddVariableCleanup(variableCleanup, simpleProxy.HoistedField); if (proxy.IsReusable) { FreeReusableHoistedField(simpleProxy.HoistedField); } } else { foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) { AddVariableCleanup(variableCleanup, field); if (proxy.IsReusable) { FreeReusableHoistedField(field); } } } } if (variableCleanup.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F))); } variableCleanup.Free(); // wrap the node in an iterator scope for debugging if (hoistedLocalsWithDebugScopes.Count != 0) { translatedStatement = MakeStateMachineScope(hoistedLocalsWithDebugScopes.ToImmutable(), translatedStatement); } hoistedLocalsWithDebugScopes.Free(); return(translatedStatement); }
public override BoundNode VisitTryStatement(BoundTryStatement node) { var tryStatementSyntax = node.Syntax; // If you add a syntax kind to the assertion below, please also ensure // that the scenario has been tested with Edit-and-Continue. Debug.Assert( tryStatementSyntax.IsKind(SyntaxKind.TryStatement) || tryStatementSyntax.IsKind(SyntaxKind.UsingStatement) || tryStatementSyntax.IsKind(SyntaxKind.ForEachStatement) || tryStatementSyntax.IsKind(SyntaxKind.ForEachVariableStatement) || tryStatementSyntax.IsKind(SyntaxKind.LocalDeclarationStatement)); BoundStatement finalizedRegion; BoundBlock rewrittenFinally; var finallyContainsAwaits = _analysis.FinallyContainsAwaits(node); if (!finallyContainsAwaits) { finalizedRegion = RewriteFinalizedRegion(node); rewrittenFinally = (BoundBlock)this.Visit(node.FinallyBlockOpt); if (rewrittenFinally == null) { return(finalizedRegion); } var asTry = finalizedRegion as BoundTryStatement; if (asTry != null) { // since finalized region is a try we can just attach finally to it Debug.Assert(asTry.FinallyBlockOpt == null); return(asTry.Update(asTry.TryBlock, asTry.CatchBlocks, rewrittenFinally, asTry.FinallyLabelOpt, asTry.PreferFaultHandler)); } else { // wrap finalizedRegion into a Try with a finally. return(_F.Try((BoundBlock)finalizedRegion, ImmutableArray <BoundCatchBlock> .Empty, rewrittenFinally)); } } // rewrite finalized region (try and catches) in the current frame var frame = PushFrame(node); finalizedRegion = RewriteFinalizedRegion(node); rewrittenFinally = (BoundBlock)this.VisitBlock(node.FinallyBlockOpt); PopFrame(); var exceptionType = _F.SpecialType(SpecialType.System_Object); var pendingExceptionLocal = new SynthesizedLocal(_F.CurrentFunction, TypeWithAnnotations.Create(exceptionType), SynthesizedLocalKind.TryAwaitPendingException, tryStatementSyntax); var finallyLabel = _F.GenerateLabel("finallyLabel"); var pendingBranchVar = new SynthesizedLocal(_F.CurrentFunction, TypeWithAnnotations.Create(_F.SpecialType(SpecialType.System_Int32)), SynthesizedLocalKind.TryAwaitPendingBranch, tryStatementSyntax); var catchAll = _F.Catch(_F.Local(pendingExceptionLocal), _F.Block()); var catchAndPendException = _F.Try( _F.Block( finalizedRegion, _F.HiddenSequencePoint(), _F.Goto(finallyLabel), PendBranches(frame, pendingBranchVar, finallyLabel)), ImmutableArray.Create(catchAll), finallyLabel: finallyLabel); BoundBlock syntheticFinallyBlock = _F.Block( _F.HiddenSequencePoint(), _F.Label(finallyLabel), rewrittenFinally, _F.HiddenSequencePoint(), UnpendException(pendingExceptionLocal), UnpendBranches( frame, pendingBranchVar, pendingExceptionLocal)); BoundStatement syntheticFinally = syntheticFinallyBlock; if (_F.CurrentFunction.IsAsync && _F.CurrentFunction.IsIterator) { // We wrap this block so that it can be processed as a finally block by async-iterator rewriting syntheticFinally = _F.ExtractedFinallyBlock(syntheticFinallyBlock); } var locals = ArrayBuilder <LocalSymbol> .GetInstance(); var statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.Add(_F.HiddenSequencePoint()); locals.Add(pendingExceptionLocal); statements.Add(_F.Assignment(_F.Local(pendingExceptionLocal), _F.Default(pendingExceptionLocal.Type))); locals.Add(pendingBranchVar); statements.Add(_F.Assignment(_F.Local(pendingBranchVar), _F.Default(pendingBranchVar.Type))); LocalSymbol returnLocal = frame.returnValue; if (returnLocal != null) { locals.Add(returnLocal); } statements.Add(catchAndPendException); statements.Add(syntheticFinally); var completeTry = _F.Block( locals.ToImmutableAndFree(), statements.ToImmutableAndFree()); return(completeTry); }
/// <summary> /// Determine if "type" inherits from or implements "baseType", ignoring constructed types, and dealing /// only with original types. /// </summary> private static bool InheritsFromOrImplementsIgnoringConstruction( this TypeSymbol type, NamedTypeSymbol baseType, CSharpCompilation compilation, ref HashSet <DiagnosticInfo> useSiteDiagnostics, ConsList <TypeSymbol> basesBeingResolved = null) { Debug.Assert(type.IsDefinition); Debug.Assert(baseType.IsDefinition); PooledHashSet <NamedTypeSymbol> interfacesLookedAt = null; ArrayBuilder <NamedTypeSymbol> baseInterfaces = null; bool baseTypeIsInterface = baseType.IsInterface; if (baseTypeIsInterface) { interfacesLookedAt = PooledHashSet <NamedTypeSymbol> .GetInstance(); baseInterfaces = ArrayBuilder <NamedTypeSymbol> .GetInstance(); } PooledHashSet <NamedTypeSymbol> visited = null; var current = type; bool result = false; while ((object)current != null) { Debug.Assert(current.IsDefinition); if (baseTypeIsInterface == current.IsInterfaceType() && current == (object)baseType) { result = true; break; } if (baseTypeIsInterface) { getBaseInterfaces(current, baseInterfaces, interfacesLookedAt, basesBeingResolved); } // NOTE(cyrusn): The base type of an 'original' type may not be 'original'. i.e. // "class Goo : IBar<int>". We must map it back to the 'original' when as we walk up // the base type hierarchy. var next = current.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, compilation, ref visited); if ((object)next == null) { current = null; } else { current = (TypeSymbol)next.OriginalDefinition; current.AddUseSiteDiagnostics(ref useSiteDiagnostics); } } visited?.Free(); if (!result && baseTypeIsInterface) { Debug.Assert(!result); while (baseInterfaces.Count != 0) { NamedTypeSymbol currentBase = baseInterfaces.Pop(); if (!currentBase.IsInterface) { continue; } Debug.Assert(currentBase.IsDefinition); if (currentBase == (object)baseType) { result = true; break; } getBaseInterfaces(currentBase, baseInterfaces, interfacesLookedAt, basesBeingResolved); } if (!result) { foreach (var candidate in interfacesLookedAt) { candidate.AddUseSiteDiagnostics(ref useSiteDiagnostics); } } } interfacesLookedAt?.Free(); baseInterfaces?.Free(); return(result);
public override void AddPreviousLocals(ArrayBuilder <Cci.ILocalDefinition> builder) { builder.AddRange(_locals); }
private BoundStatement UnpendBranches( AwaitFinallyFrame frame, SynthesizedLocal pendingBranchVar, SynthesizedLocal pendingException) { var parent = frame.ParentOpt; // handle proxy labels if have any var proxiedLabels = frame.proxiedLabels; // skip 0 - it means we took no explicit branches int i = 1; var cases = ArrayBuilder <SyntheticBoundNodeFactory.SyntheticSwitchSection> .GetInstance(); if (proxiedLabels != null) { for (int cnt = proxiedLabels.Count; i <= cnt; i++) { var target = proxiedLabels[i - 1]; var parentProxy = parent.ProxyLabelIfNeeded(target); var caseStatement = _F.SwitchSection(i, _F.Goto(parentProxy)); cases.Add(caseStatement); } } if (frame.returnProxyLabel != null) { BoundLocal pendingValue = null; if (frame.returnValue != null) { pendingValue = _F.Local(frame.returnValue); } SynthesizedLocal returnValue; BoundStatement unpendReturn; var returnLabel = parent.ProxyReturnIfNeeded(_F.CurrentFunction, pendingValue, out returnValue); if (returnLabel == null) { unpendReturn = new BoundReturnStatement(_F.Syntax, RefKind.None, pendingValue); } else { if (pendingValue == null) { unpendReturn = _F.Goto(returnLabel); } else { unpendReturn = _F.Block( _F.Assignment( _F.Local(returnValue), pendingValue), _F.Goto(returnLabel)); } } var caseStatement = _F.SwitchSection(i, unpendReturn); cases.Add(caseStatement); } return(_F.Switch(_F.Local(pendingBranchVar), cases.ToImmutableAndFree())); }
private void OnSymbolEnd(SymbolAnalysisContext symbolEndContext, bool hasUnsupportedOperation) { if (hasUnsupportedOperation) { return; } if (symbolEndContext.Symbol.GetAttributes().Any(a => a.AttributeClass == _structLayoutAttributeType)) { // Bail out for types with 'StructLayoutAttribute' as the ordering of the members is critical, // and removal of unused members might break semantics. return; } // Report diagnostics for unused candidate members. var first = true; PooledHashSet <ISymbol> symbolsReferencedInDocComments = null; ArrayBuilder <string> debuggerDisplayAttributeArguments = null; try { var entryPoint = symbolEndContext.Compilation.GetEntryPoint(symbolEndContext.CancellationToken); var namedType = (INamedTypeSymbol)symbolEndContext.Symbol; foreach (var member in namedType.GetMembers()) { if (SymbolEqualityComparer.Default.Equals(entryPoint, member)) { continue; } // Check if the underlying member is neither read nor a readable reference to the member is taken. // If so, we flag the member as either unused (never written) or unread (written but not read). if (TryRemove(member, out var valueUsageInfo) && !valueUsageInfo.IsReadFrom()) { Debug.Assert(IsCandidateSymbol(member)); Debug.Assert(!member.IsImplicitlyDeclared); if (first) { // Bail out if there are syntax errors in any of the declarations of the containing type. // Note that we check this only for the first time that we report an unused or unread member for the containing type. if (HasSyntaxErrors(namedType, symbolEndContext.CancellationToken)) { return; } // Compute the set of candidate symbols referenced in all the documentation comments within the named type declarations. // This set is computed once and used for all the iterations of the loop. symbolsReferencedInDocComments = GetCandidateSymbolsReferencedInDocComments(namedType, symbolEndContext.Compilation, symbolEndContext.CancellationToken); // Compute the set of string arguments to DebuggerDisplay attributes applied to any symbol within the named type declaration. // These strings may have an embedded reference to the symbol. // This set is computed once and used for all the iterations of the loop. debuggerDisplayAttributeArguments = GetDebuggerDisplayAttributeArguments(namedType); first = false; } // Simple heuristic for members referenced in DebuggerDisplayAttribute's string argument: // bail out if any of the DebuggerDisplay string arguments contains the member name. // In future, we can consider improving this heuristic to parse the embedded expression // and resolve symbol references. if (debuggerDisplayAttributeArguments.Any(arg => arg.Contains(member.Name))) { continue; } // Report IDE0051 or IDE0052 based on whether the underlying member has any Write/WritableRef/NonReadWriteRef references or not. var rule = !valueUsageInfo.IsWrittenTo() && !valueUsageInfo.IsNameOnly() && !symbolsReferencedInDocComments.Contains(member) ? s_removeUnusedMembersRule : s_removeUnreadMembersRule; // Do not flag write-only properties that are not read. // Write-only properties are assumed to have side effects // visible through other means than a property getter. if (rule == s_removeUnreadMembersRule && member is IPropertySymbol property && property.IsWriteOnly) { continue; } // Most of the members should have a single location, except for partial methods. // We report the diagnostic on the first location of the member. var diagnostic = DiagnosticHelper.CreateWithMessage( rule, member.Locations[0], rule.GetEffectiveSeverity(symbolEndContext.Compilation.Options), additionalLocations: null, properties: null, GetMessage(rule, member)); symbolEndContext.ReportDiagnostic(diagnostic); } } } finally { symbolsReferencedInDocComments?.Free(); debuggerDisplayAttributeArguments?.Free(); } return; }
internal static bool TryGetTupleFieldValues(this DkmClrValue tuple, int cardinality, ArrayBuilder <string> values, DkmInspectionContext inspectionContext) { while (true) { var type = tuple.Type.GetLmrType(); int n = Math.Min(cardinality, TupleFieldRestPosition - 1); for (int index = 0; index < n; index++) { var fieldName = GetTupleFieldName(index); var fieldInfo = type.GetTupleField(fieldName); if (fieldInfo == null) { return(false); } var value = tuple.GetFieldValue(fieldName, inspectionContext); var str = value.GetValueString(inspectionContext, Formatter.NoFormatSpecifiers); values.Add(str); } cardinality -= n; if (cardinality == 0) { return(true); } var restInfo = type.GetTupleField(TypeHelpers.TupleFieldRestName); if (restInfo == null) { return(false); } tuple = tuple.GetFieldValue(TupleFieldRestName, inspectionContext); } }
internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder <SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); var type = this.Type; if (type.TypeSymbol.ContainsDynamic()) { var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(type.TypeSymbol, type.CustomModifiers.Length)); } if (type.TypeSymbol.ContainsTupleNames()) { AddSynthesizedAttribute(ref attributes, DeclaringCompilation.SynthesizeTupleNamesAttribute(type.TypeSymbol)); } AddSynthesizedNonNullTypesAttributeForMember(ref attributes); if (type.ContainsNullableReferenceTypes()) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttribute(this, type)); } }
internal static void AppendTypeMembers( this Type type, ArrayBuilder <MemberAndDeclarationInfo> includedMembers, Predicate <MemberInfo> predicate, Type declaredType, DkmClrAppDomain appDomain, bool includeInherited, bool hideNonPublic) { Debug.Assert(!type.IsInterface); var memberLocation = DeclarationInfo.FromSubTypeOfDeclaredType; var previousDeclarationMap = includeInherited ? new Dictionary <string, DeclarationInfo>() : null; int inheritanceLevel = 0; while (!type.IsObject()) { if (type.Equals(declaredType)) { Debug.Assert(memberLocation == DeclarationInfo.FromSubTypeOfDeclaredType); memberLocation = DeclarationInfo.FromDeclaredTypeOrBase; } // Get the state from DebuggerBrowsableAttributes for the members of the current type. var browsableState = DkmClrType.Create(appDomain, type).GetDebuggerBrowsableAttributeState(); // Hide non-public members if hideNonPublic is specified (intended to reflect the // DkmInspectionContext's DkmEvaluationFlags), and the type is from an assembly // with no symbols. var hideNonPublicBehavior = DeclarationInfo.None; if (hideNonPublic) { var moduleInstance = appDomain.FindClrModuleInstance(type.Module.ModuleVersionId); if (moduleInstance == null || moduleInstance.Module == null) { // Synthetic module or no symbols loaded. hideNonPublicBehavior = DeclarationInfo.HideNonPublic; } } foreach (var member in type.GetMembers(MemberBindingFlags)) { if (!predicate(member)) { continue; } var memberName = member.Name; // This represents information about the immediately preceding (more derived) // declaration with the same name as the current member. var previousDeclaration = DeclarationInfo.None; var memberNameAlreadySeen = false; if (includeInherited) { memberNameAlreadySeen = previousDeclarationMap.TryGetValue(memberName, out previousDeclaration); if (memberNameAlreadySeen) { // There was a name conflict, so we'll need to include the declaring // type of the member to disambiguate. previousDeclaration |= DeclarationInfo.IncludeTypeInMemberName; } // Update previous member with name hiding (casting) and declared location information for next time. previousDeclarationMap[memberName] = (previousDeclaration & ~(DeclarationInfo.RequiresExplicitCast | DeclarationInfo.FromSubTypeOfDeclaredType)) | member.AccessingBaseMemberWithSameNameRequiresExplicitCast() | memberLocation; } Debug.Assert(memberNameAlreadySeen != (previousDeclaration == DeclarationInfo.None)); // Decide whether to include this member in the list of members to display. if (!memberNameAlreadySeen || previousDeclaration.IsSet(DeclarationInfo.RequiresExplicitCast)) { DkmClrDebuggerBrowsableAttributeState?browsableStateValue = null; if (browsableState != null) { DkmClrDebuggerBrowsableAttributeState value; if (browsableState.TryGetValue(memberName, out value)) { browsableStateValue = value; } } if (memberLocation.IsSet(DeclarationInfo.FromSubTypeOfDeclaredType)) { // If the current type is a sub-type of the declared type, then // we always need to insert a cast to access the member previousDeclaration |= DeclarationInfo.RequiresExplicitCast; } else if (previousDeclaration.IsSet(DeclarationInfo.FromSubTypeOfDeclaredType)) { // If the immediately preceding member (less derived) was // declared on a sub-type of the declared type, then we'll // ignore the casting bit. Accessing a member through the // declared type is the same as casting to that type, so // the cast would be redundant. previousDeclaration &= ~DeclarationInfo.RequiresExplicitCast; } previousDeclaration |= hideNonPublicBehavior; includedMembers.Add(new MemberAndDeclarationInfo(member, browsableStateValue, previousDeclaration, inheritanceLevel)); } } if (!includeInherited) { break; } type = type.BaseType; inheritanceLevel++; } includedMembers.Sort(MemberAndDeclarationInfo.Comparer); }
public override DictionaryAnalysisData <AnalysisEntity, TValue> Merge(DictionaryAnalysisData <AnalysisEntity, TValue> map1, DictionaryAnalysisData <AnalysisEntity, TValue> map2) { AssertValidAnalysisData(map1); AssertValidAnalysisData(map2); var resultMap = new DictionaryAnalysisData <AnalysisEntity, TValue>(); using var newKeys = PooledHashSet <AnalysisEntity> .GetInstance(); using var valuesToMergeBuilder = ArrayBuilder <TValue> .GetInstance(5); var map2LookupIgnoringInstanceLocation = map2.Keys.Where(IsAnalysisEntityForFieldOrProperty) .ToLookup(entity => entity.EqualsIgnoringInstanceLocationId); foreach (var entry1 in map1) { AnalysisEntity key1 = entry1.Key; TValue value1 = entry1.Value; if (map2LookupIgnoringInstanceLocation.Count > 0 && IsAnalysisEntityForFieldOrProperty(key1)) { var equivalentKeys2 = map2LookupIgnoringInstanceLocation[key1.EqualsIgnoringInstanceLocationId]; if (!equivalentKeys2.Any()) { TValue mergedValue = GetMergedValueForEntityPresentInOneMap(key1, value1); Debug.Assert(!map2.ContainsKey(key1)); Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); AddNewEntryToResultMap(key1, mergedValue); continue; } foreach (AnalysisEntity key2 in equivalentKeys2) { // Confirm that key2 and key1 are indeed EqualsIgnoringInstanceLocation // This ensures that we handle hash code clashes of EqualsIgnoringInstanceLocationId. if (!key1.EqualsIgnoringInstanceLocation(key2)) { continue; } TValue value2 = map2[key2]; valuesToMergeBuilder.Clear(); valuesToMergeBuilder.Add(value1); valuesToMergeBuilder.Add(value2); if (key1.InstanceLocation.Equals(key2.InstanceLocation)) { var mergedValue = GetMergedValue(valuesToMergeBuilder); AddNewEntryToResultMap(key1, mergedValue); } else { if (key1.SymbolOpt == null || !Equals(key1.SymbolOpt, key2.SymbolOpt)) { // PERF: Do not add a new key-value pair to the resultMap for unrelated entities or non-symbol based entities. continue; } AnalysisEntity mergedKey = key1.WithMergedInstanceLocation(key2); var isExistingKeyInInput = false; var isExistingKeyInResult = false; if (resultMap.TryGetValue(mergedKey, out var existingValue)) { valuesToMergeBuilder.Add(existingValue); isExistingKeyInResult = true; } if (map1.TryGetValue(mergedKey, out existingValue)) { valuesToMergeBuilder.Add(existingValue); isExistingKeyInInput = true; } if (map2.TryGetValue(mergedKey, out existingValue)) { valuesToMergeBuilder.Add(existingValue); isExistingKeyInInput = true; } var isCandidateToBeSkipped = !isExistingKeyInInput && !isExistingKeyInResult; if (isCandidateToBeSkipped && CanSkipNewEntity(mergedKey)) { // PERF: Do not add a new key-value pair to the resultMap if the key is not reachable from tracked entities and PointsTo values. continue; } var mergedValue = GetMergedValue(valuesToMergeBuilder); Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); if (isCandidateToBeSkipped && CanSkipNewEntry(mergedKey, mergedValue)) { // PERF: Do not add a new key-value pair to the resultMap if the value can be skipped. continue; } if (!isExistingKeyInInput) { newKeys.Add(mergedKey); } AddNewEntryToResultMap(mergedKey, mergedValue, isNewKey: !isExistingKeyInInput); } } } else if (map2.TryGetValue(key1, out var value2)) { TValue mergedValue = ValueDomain.Merge(value1, value2); Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); AddNewEntryToResultMap(key1, mergedValue); continue; } if (!resultMap.ContainsKey(key1)) { TValue mergedValue = GetMergedValueForEntityPresentInOneMap(key1, value1); Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); AddNewEntryToResultMap(key1, mergedValue); } } foreach (var kvp in map2) { var key2 = kvp.Key; var value2 = kvp.Value; if (!resultMap.ContainsKey(key2)) { TValue mergedValue = GetMergedValueForEntityPresentInOneMap(key2, value2); Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); AddNewEntryToResultMap(key2, mergedValue); } } foreach (var newKey in newKeys) { Debug.Assert(!map1.ContainsKey(newKey)); Debug.Assert(!map2.ContainsKey(newKey)); var value = resultMap[newKey]; if (ReferenceEquals(value, GetDefaultValue(newKey))) { resultMap.Remove(newKey); } else { OnNewMergedValue(value); } } Debug.Assert(Compare(map1, resultMap) <= 0); Debug.Assert(Compare(map2, resultMap) <= 0); AssertValidAnalysisData(resultMap); return(resultMap);
internal ImmutableArray <TypeParameterConstraintClause> BindTypeParameterConstraintClauses( Symbol containingSymbol, ImmutableArray <TypeParameterSymbol> typeParameters, TypeParameterListSyntax typeParameterList, SyntaxList <TypeParameterConstraintClauseSyntax> clauses, ref IReadOnlyDictionary <TypeParameterSymbol, bool> isValueTypeOverride, DiagnosticBag diagnostics, bool isForOverride = false) { Debug.Assert(this.Flags.Includes(BinderFlags.GenericConstraintsClause)); RoslynDebug.Assert((object)containingSymbol != null); Debug.Assert((containingSymbol.Kind == SymbolKind.NamedType) || (containingSymbol.Kind == SymbolKind.Method)); Debug.Assert(typeParameters.Length > 0); Debug.Assert(clauses.Count > 0); int n = typeParameters.Length; // Create a map from type parameter name to ordinal. // No need to report duplicate names since duplicates // are reported when the type parameters are bound. var names = new Dictionary <string, int>(n, StringOrdinalComparer.Instance); foreach (var typeParameter in typeParameters) { var name = typeParameter.Name; if (!names.ContainsKey(name)) { names.Add(name, names.Count); } } // An array of constraint clauses, one for each type parameter, indexed by ordinal. var results = ArrayBuilder <TypeParameterConstraintClause?> .GetInstance(n, fillWithValue : null); var syntaxNodes = ArrayBuilder <ArrayBuilder <TypeConstraintSyntax>?> .GetInstance(n, fillWithValue : null); // Bind each clause and add to the results. foreach (var clause in clauses) { var name = clause.Name.Identifier.ValueText; RoslynDebug.Assert(name is object); int ordinal; if (names.TryGetValue(name, out ordinal)) { Debug.Assert(ordinal >= 0); Debug.Assert(ordinal < n); (TypeParameterConstraintClause constraintClause, ArrayBuilder <TypeConstraintSyntax>?typeConstraintNodes) = this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, diagnostics); if (results[ordinal] == null) { results[ordinal] = constraintClause; syntaxNodes[ordinal] = typeConstraintNodes; } else { // "A constraint clause has already been specified for type parameter '{0}'. ..." diagnostics.Add(ErrorCode.ERR_DuplicateConstraintClause, clause.Name.Location, name); typeConstraintNodes?.Free(); } } else { // Unrecognized type parameter. Don't bother binding the constraints // (the ": I<U>" in "where U : I<U>") since that will lead to additional // errors ("type or namespace 'U' could not be found") if the type // parameter is referenced in the constraints. // "'{1}' does not define type parameter '{0}'" diagnostics.Add(ErrorCode.ERR_TyVarNotFoundInConstraint, clause.Name.Location, name, containingSymbol.ConstructedFrom()); } } // Add default values for type parameters without constraint clauses. for (int i = 0; i < n; i++) { if (results[i] == null) { results[i] = GetDefaultTypeParameterConstraintClause(typeParameterList.Parameters[i], isForOverride); } } TypeParameterConstraintClause.AdjustConstraintTypes(containingSymbol, typeParameters, results, ref isValueTypeOverride); RemoveInvalidConstraints(typeParameters, results !, syntaxNodes, diagnostics); foreach (var typeConstraintsSyntaxes in syntaxNodes) { typeConstraintsSyntaxes?.Free(); } syntaxNodes.Free(); return(results.ToImmutableAndFree() !); }
/// <remarks> /// NOTE: Keep this method in sync with AnalyzeImplicitUserDefinedConversion. /// </remarks> protected UserDefinedConversionResult AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType(TypeSymbol source, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: The governing type of a switch statement is established by the switch expression. // SPEC: 1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint, // SPEC: long, ulong, bool, char, string, or an enum-type, or if it is the nullable type // SPEC: corresponding to one of these types, then that is the governing type of the switch statement. // SPEC: 2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the // SPEC: type of the switch expression to one of the following possible governing types: // SPEC: sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type // SPEC: corresponding to one of those types // NOTE: This method implements part (2) above, it should be called only if (1) is false for source type. Debug.Assert((object)source != null); Debug.Assert(!source.IsValidV6SwitchGoverningType()); // NOTE: For (2) we use an approach similar to native compiler's approach, but call into the common code for analyzing user defined implicit conversions. // NOTE: (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type. // NOTE: (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type, // NOTE: we compute these from the source type to ANY target type. // NOTE: (c) From the conversions in U, select the most specific of them that targets a valid switch governing type // SPEC VIOLATION: Because we use the same strategy for computing the most specific conversion, as the Dev10 compiler did (in fact // SPEC VIOLATION: we share the code), we inherit any spec deviances in that analysis. Specifically, the analysis only considers // SPEC VIOLATION: which conversion has the least amount of lifting, where a conversion may be considered to be in unlifted form, // SPEC VIOLATION: half-lifted form (only the argument type or return type is lifted) or fully lifted form. The most specific computation // SPEC VIOLATION: looks for a unique conversion that is least lifted. The spec, on the other hand, requires that the conversion // SPEC VIOLATION: be *unique*, not merely most use the least amount of lifting among the applicable conversions. // SPEC VIOLATION: This introduces a SPEC VIOLATION for the following tests in the native compiler: // NOTE: // See test SwitchTests.CS0166_AggregateTypeWithMultipleImplicitConversions_07 // NOTE: struct Conv // NOTE: { // NOTE: public static implicit operator int (Conv C) { return 1; } // NOTE: public static implicit operator int (Conv? C2) { return 0; } // NOTE: public static int Main() // NOTE: { // NOTE: Conv? D = new Conv(); // NOTE: switch(D) // NOTE: { ... // SPEC VIOLATION: Native compiler allows the above code to compile // SPEC VIOLATION: even though there are two user-defined implicit conversions: // SPEC VIOLATION: 1) To int type (applicable in normal form): public static implicit operator int (Conv? C2) // SPEC VIOLATION: 2) To int? type (applicable in lifted form): public static implicit operator int (Conv C) // NOTE: // See also test SwitchTests.TODO // NOTE: struct Conv // NOTE: { // NOTE: public static implicit operator int? (Conv C) { return 1; } // NOTE: public static implicit operator string (Conv? C2) { return 0; } // NOTE: public static int Main() // NOTE: { // NOTE: Conv? D = new Conv(); // NOTE: switch(D) // NOTE: { ... // SPEC VIOLATION: Native compiler allows the above code to compile too // SPEC VIOLATION: even though there are two user-defined implicit conversions: // SPEC VIOLATION: 1) To string type (applicable in normal form): public static implicit operator string (Conv? C2) // SPEC VIOLATION: 2) To int? type (applicable in half-lifted form): public static implicit operator int? (Conv C) // SPEC VIOLATION: This occurs because the native compiler compares the applicable conversions to find one with the least amount // SPEC VIOLATION: of lifting, ignoring whether the return types are the same or not. // SPEC VIOLATION: We do the same to maintain compatibility with the native compiler. // (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type. var d = ArrayBuilder <NamedTypeSymbol> .GetInstance(); ComputeUserDefinedImplicitConversionTypeSet(source, t: null, d: d, useSiteDiagnostics: ref useSiteDiagnostics); // (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type, // we compute these from the source type to ANY target type. We will filter out those that are valid switch governing // types later. var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance(); ComputeApplicableUserDefinedImplicitConversionSet(null, source, target: null, d: d, u: ubuild, useSiteDiagnostics: ref useSiteDiagnostics, allowAnyTarget: true); d.Free(); ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree(); // (c) Find that conversion with the least amount of lifting int?best = MostSpecificConversionOperator(conv => conv.ToType.IsValidV6SwitchGoverningType(isTargetTypeOfUserDefinedOp: true), u); if (best != null) { return(UserDefinedConversionResult.Valid(u, best.Value)); } return(UserDefinedConversionResult.NoApplicableOperators(u)); }
private (TypeParameterConstraintClause, ArrayBuilder <TypeConstraintSyntax>?) BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; ArrayBuilder <TypeWithAnnotations>? constraintTypes = null; ArrayBuilder <TypeConstraintSyntax>?syntaxBuilder = null; SeparatedSyntaxList <TypeParameterConstraintSyntax> constraintsSyntax = constraintClauseSyntax.Constraints; Debug.Assert(!InExecutableBinder); // Cannot eagerly report diagnostics handled by LazyMissingNonNullTypesContextDiagnosticInfo bool hasTypeLikeConstraint = false; bool reportedOverrideWithConstraints = false; for (int i = 0, n = constraintsSyntax.Count; i < n; i++) { var syntax = constraintsSyntax[i]; switch (syntax.Kind()) { case SyntaxKind.ClassConstraint: hasTypeLikeConstraint = true; if (i != 0) { if (!reportedOverrideWithConstraints) { reportTypeConstraintsMustBeUniqueAndFirst(syntax, diagnostics); } if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0) { continue; } } var constraintSyntax = (ClassOrStructConstraintSyntax)syntax; SyntaxToken questionToken = constraintSyntax.QuestionToken; if (questionToken.IsKind(SyntaxKind.QuestionToken)) { constraints |= TypeParameterConstraintKind.NullableReferenceType; if (isForOverride) { reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics); } else { LazyMissingNonNullTypesContextDiagnosticInfo.ReportNullableReferenceTypesIfNeeded(AreNullableAnnotationsEnabled(questionToken), questionToken.GetLocation(), diagnostics); } } else if (isForOverride || AreNullableAnnotationsEnabled(constraintSyntax.ClassOrStructKeyword)) { constraints |= TypeParameterConstraintKind.NotNullableReferenceType; } else { constraints |= TypeParameterConstraintKind.ReferenceType; } continue; case SyntaxKind.StructConstraint: hasTypeLikeConstraint = true; if (i != 0) { if (!reportedOverrideWithConstraints) { reportTypeConstraintsMustBeUniqueAndFirst(syntax, diagnostics); } if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0) { continue; } } constraints |= TypeParameterConstraintKind.ValueType; continue; case SyntaxKind.ConstructorConstraint: if (isForOverride) { reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics); continue; } if ((constraints & TypeParameterConstraintKind.ValueType) != 0) { diagnostics.Add(ErrorCode.ERR_NewBoundWithVal, syntax.GetFirstToken().GetLocation()); } if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0) { diagnostics.Add(ErrorCode.ERR_NewBoundWithUnmanaged, syntax.GetFirstToken().GetLocation()); } if (i != n - 1) { diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.Constructor; continue; case SyntaxKind.DefaultConstraint: if (!isForOverride) { diagnostics.Add(ErrorCode.ERR_DefaultConstraintOverrideOnly, syntax.GetLocation()); } if (i != 0) { if (!reportedOverrideWithConstraints) { reportTypeConstraintsMustBeUniqueAndFirst(syntax, diagnostics); } if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0) { continue; } } constraints |= TypeParameterConstraintKind.Default; continue; case SyntaxKind.TypeConstraint: if (isForOverride) { reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics); } else { hasTypeLikeConstraint = true; if (constraintTypes == null) { constraintTypes = ArrayBuilder <TypeWithAnnotations> .GetInstance(); syntaxBuilder = ArrayBuilder <TypeConstraintSyntax> .GetInstance(); } var typeConstraintSyntax = (TypeConstraintSyntax)syntax; var typeSyntax = typeConstraintSyntax.Type; var type = BindTypeOrConstraintKeyword(typeSyntax, diagnostics, out ConstraintContextualKeyword keyword); switch (keyword) { case ConstraintContextualKeyword.Unmanaged: if (i != 0) { reportTypeConstraintsMustBeUniqueAndFirst(typeSyntax, diagnostics); continue; } // This should produce diagnostics if the types are missing GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType, diagnostics, typeSyntax); GetSpecialType(SpecialType.System_ValueType, diagnostics, typeSyntax); constraints |= TypeParameterConstraintKind.Unmanaged; continue; case ConstraintContextualKeyword.NotNull: if (i != 0) { reportTypeConstraintsMustBeUniqueAndFirst(typeSyntax, diagnostics); } constraints |= TypeParameterConstraintKind.NotNull; continue; case ConstraintContextualKeyword.None: break; default: throw ExceptionUtilities.UnexpectedValue(keyword); } constraintTypes.Add(type); syntaxBuilder !.Add(typeConstraintSyntax); } continue; default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } } if (!isForOverride && !hasTypeLikeConstraint && !AreNullableAnnotationsEnabled(typeParameterSyntax.Identifier)) { constraints |= TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType; } Debug.Assert(!isForOverride || (constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)); return(TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray <TypeWithAnnotations> .Empty), syntaxBuilder);
private static ImmutableArray <CodeFix> GetConfigurations(Project project, IEnumerable <Diagnostic> diagnostics, CancellationToken cancellationToken) { var result = ArrayBuilder <CodeFix> .GetInstance(); foreach (var diagnostic in diagnostics) { // First get all the relevant code style options for the diagnostic. var codeStyleOptions = ConfigurationUpdater.GetCodeStyleOptionsForDiagnostic(diagnostic, project); if (codeStyleOptions.IsEmpty) { continue; } // For each code style option, create a top level code action with nested code actions for every valid option value. // For example, if the option value is CodeStyleOption<bool>, we will have two nested actions, one for 'true' setting and one // for 'false' setting. If the option value is CodeStyleOption<SomeEnum>, we will have a nested action for each enum field. using var _ = ArrayBuilder <CodeAction> .GetInstance(out var nestedActions); var optionSet = project.Solution.Workspace.Options; var hasMultipleOptions = codeStyleOptions.Length > 1; foreach (var(optionKey, codeStyleOption, editorConfigLocation, perLanguageOption) in codeStyleOptions.OrderBy(t => t.optionKey.Option.Name)) { var topLevelAction = GetCodeActionForCodeStyleOption(optionKey, codeStyleOption, editorConfigLocation, diagnostic, perLanguageOption, optionSet, hasMultipleOptions); if (topLevelAction != null) { nestedActions.Add(topLevelAction); } } if (nestedActions.Count != 0) { // Wrap actions by another level if the diagnostic ID has multiple associated code style options to reduce clutter. var resultCodeAction = nestedActions.Count > 1 ? new TopLevelConfigureCodeStyleOptionCodeAction(diagnostic, nestedActions.ToImmutable()) : nestedActions.Single(); result.Add(new CodeFix(project, resultCodeAction, diagnostic)); } } return(result.ToImmutableAndFree()); // Local functions TopLevelConfigureCodeStyleOptionCodeAction GetCodeActionForCodeStyleOption( OptionKey optionKey, ICodeStyleOption codeStyleOption, IEditorConfigStorageLocation2 editorConfigLocation, Diagnostic diagnostic, bool isPerLanguage, OptionSet optionSet, bool hasMultipleOptions) { // Add a code action for every valid value of the given code style option. // We only support light-bulb configuration of code style options with boolean or enum values. using var _ = ArrayBuilder <CodeAction> .GetInstance(out var nestedActions); var severity = codeStyleOption.Notification.ToEditorConfigString(); string optionName = null; if (codeStyleOption.Value is bool) { foreach (var boolValue in s_boolValues) { AddCodeActionWithOptionValue(codeStyleOption, boolValue); } } else if (codeStyleOption.Value?.GetType() is Type t && t.IsEnum) { foreach (var enumValue in Enum.GetValues(t)) { AddCodeActionWithOptionValue(codeStyleOption, enumValue); } } if (nestedActions.Count > 0) { // If this is not a unique code style option for the diagnostic, use the optionName as the code action title. // In that case, we will already have a containing top level action for the diagnostic. // Otherwise, use the diagnostic information in the title. return(hasMultipleOptions ? new TopLevelConfigureCodeStyleOptionCodeAction(optionName, nestedActions.ToImmutable()) : new TopLevelConfigureCodeStyleOptionCodeAction(diagnostic, nestedActions.ToImmutable())); } return(null); // Local functions void AddCodeActionWithOptionValue(ICodeStyleOption codeStyleOption, object newValue) { // Create a new code style option value with the newValue var configuredCodeStyleOption = codeStyleOption.WithValue(newValue); // Try to get the parsed editorconfig string representation of the new code style option value if (ConfigurationUpdater.TryGetEditorConfigStringParts(configuredCodeStyleOption, editorConfigLocation, optionSet, out var parts)) { // We expect all code style values for same code style option to have the same editorconfig option name. Debug.Assert(optionName == null || optionName == parts.optionName); optionName ??= parts.optionName; // Add code action to configure the optionValue. nestedActions.Add( new SolutionChangeAction( parts.optionValue, solution => ConfigurationUpdater.ConfigureCodeStyleOptionAsync(parts.optionName, parts.optionValue, diagnostic, isPerLanguage, project, cancellationToken))); } } } }
private ImmutableArray <ParameterSymbol> MakeParameters( CSharpCompilation compilation, UnboundLambda unboundLambda, ImmutableArray <TypeWithAnnotations> parameterTypes, ImmutableArray <RefKind> parameterRefKinds ) { Debug.Assert(parameterTypes.Length == parameterRefKinds.Length); if (!unboundLambda.HasSignature || unboundLambda.ParameterCount == 0) { // The parameters may be omitted in source, but they are still present on the symbol. return(parameterTypes.SelectAsArray( (type, ordinal, arg) => SynthesizedParameterSymbol.Create( arg.owner, type, ordinal, arg.refKinds[ordinal], GeneratedNames.LambdaCopyParameterName(ordinal) ), // Make sure nothing binds to this. (owner: this, refKinds: parameterRefKinds) )); } var builder = ArrayBuilder <ParameterSymbol> .GetInstance(unboundLambda.ParameterCount); var hasExplicitlyTypedParameterList = unboundLambda.HasExplicitlyTypedParameterList; var numDelegateParameters = parameterTypes.Length; for (int p = 0; p < unboundLambda.ParameterCount; ++p) { // If there are no types given in the lambda then used the delegate type. // If the lambda is typed then the types probably match the delegate types; // if they do not, use the lambda types for binding. Either way, if we // can, then we use the lambda types. (Whatever you do, do not use the names // in the delegate parameters; they are not in scope!) TypeWithAnnotations type; RefKind refKind; if (hasExplicitlyTypedParameterList) { type = unboundLambda.ParameterTypeWithAnnotations(p); refKind = unboundLambda.RefKind(p); } else if (p < numDelegateParameters) { type = parameterTypes[p]; refKind = parameterRefKinds[p]; } else { type = TypeWithAnnotations.Create( new ExtendedErrorTypeSymbol( compilation, name: string.Empty, arity: 0, errorInfo: null ) ); refKind = RefKind.None; } var name = unboundLambda.ParameterName(p); var location = unboundLambda.ParameterLocation(p); var locations = location == null ? ImmutableArray <Location> .Empty : ImmutableArray.Create <Location>(location); var parameter = new SourceSimpleParameterSymbol( owner: this, type, ordinal: p, refKind, name, unboundLambda.ParameterIsDiscard(p), locations ); builder.Add(parameter); } var result = builder.ToImmutableAndFree(); return(result); }
internal sealed override void AddSynthesizedAttributes(ModuleCompilationState compilationState, ref ArrayBuilder <SynthesizedAttributeData> attributes) { base.AddSynthesizedAttributes(compilationState, ref attributes); if (_debuggerHidden) { var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor)); } if (this.ReturnType.ContainsDynamic()) { var compilation = this.DeclaringCompilation; AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.ReturnType, this.ReturnTypeCustomModifiers.Length)); } }
private void AddDebuggerDisplayAttributeArguments(INamedTypeSymbol namedTypeSymbol, ArrayBuilder <string> builder) { AddDebuggerDisplayAttributeArgumentsCore(namedTypeSymbol, builder); foreach (var member in namedTypeSymbol.GetMembers()) { switch (member) { case INamedTypeSymbol nestedType: AddDebuggerDisplayAttributeArguments(nestedType, builder); break; case IPropertySymbol _: case IFieldSymbol _: AddDebuggerDisplayAttributeArgumentsCore(member, builder); break; } } }
/// <remarks> /// NOTE: Keep this method in sync with <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>. /// </remarks> private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions( BoundExpression sourceExpression, TypeSymbol source, TypeSymbol target, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(sourceExpression != null || (object)source != null); Debug.Assert((object)target != null); // User-defined conversions that involve generics can be quite strange. There // are two basic problems: first, that generic user-defined conversions can be // "shadowed" by built-in conversions, and second, that generic user-defined // conversions can make conversions that would never have been legal user-defined // conversions if declared non-generically. I call this latter kind of conversion // a "suspicious" conversion. // // The shadowed conversions are easily dealt with: // // SPEC: If a predefined implicit conversion exists from a type S to type T, // SPEC: all user-defined conversions, implicit or explicit, are ignored. // SPEC: If a predefined explicit conversion exists from a type S to type T, // SPEC: any user-defined explicit conversion from S to T are ignored. // // The rule above can come into play in cases like: // // sealed class C<T> { public static implicit operator T(C<T> c) { ... } } // C<object> c = whatever; // object o = c; // // The built-in implicit conversion from C<object> to object must shadow // the user-defined implicit conversion. // // The caller of this method checks for user-defined conversions *after* // predefined implicit conversions, so we already know that if we got here, // there was no predefined implicit conversion. // // Note that a user-defined *implicit* conversion may win over a built-in // *explicit* conversion by the rule given above. That is, if we created // an implicit conversion from T to C<T>, then the user-defined implicit // conversion from object to C<object> could be valid, even though that // would be "replacing" a built-in explicit conversion with a user-defined // implicit conversion. This is one of the "suspicious" conversions, // as it would not be legal to declare a user-defined conversion from // object in a non-generic type. // // The way the native compiler handles suspicious conversions involving // interfaces is neither sensible nor in line with the rules in the // specification. It is not clear at this time whether we should be exactly // matching the native compiler, the specification, or neither, in Roslyn. // Spec (6.4.4 User-defined implicit conversions) // A user-defined implicit conversion from an expression E to type T is processed as follows: // SPEC: Find the set of types D from which user-defined conversion operators... var d = ArrayBuilder <NamedTypeSymbol> .GetInstance(); ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteDiagnostics); // SPEC: Find the set of applicable user-defined and lifted conversion operators, U... var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance(); ComputeApplicableUserDefinedImplicitConversionSet(sourceExpression, source, target, d, ubuild, ref useSiteDiagnostics); d.Free(); ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree(); // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs. if (u.Length == 0) { return(UserDefinedConversionResult.NoApplicableOperators(u)); } // SPEC: Find the most specific source type SX of the operators in U... TypeSymbol sx = MostSpecificSourceTypeForImplicitUserDefinedConversion(u, source, ref useSiteDiagnostics); if ((object)sx == null) { return(UserDefinedConversionResult.NoBestSourceType(u)); } // SPEC: Find the most specific target type TX of the operators in U... TypeSymbol tx = MostSpecificTargetTypeForImplicitUserDefinedConversion(u, target, ref useSiteDiagnostics); if ((object)tx == null) { return(UserDefinedConversionResult.NoBestTargetType(u)); } int?best = MostSpecificConversionOperator(sx, tx, u); if (best == null) { return(UserDefinedConversionResult.Ambiguous(u)); } return(UserDefinedConversionResult.Valid(u, best.Value)); }