/// <summary> /// Force a variable to have a slot. Returns -1 if the variable has an empty struct type. /// </summary> protected virtual int GetOrCreateSlot( Symbol symbol, int containingSlot = 0, bool forceSlotEvenIfEmpty = false, bool createIfMissing = true ) { Debug.Assert(containingSlot >= 0); Debug.Assert(symbol != null); if (symbol.Kind == SymbolKind.RangeVariable) { return(-1); } containingSlot = DescendThroughTupleRestFields( ref symbol, containingSlot, forceContainingSlotsToExist: true ); if (containingSlot < 0) { // Error case. Diagnostics should already have been produced. return(-1); } VariableIdentifier identifier = new VariableIdentifier(symbol, containingSlot); int slot; // Since analysis may proceed in multiple passes, it is possible the slot is already assigned. if (!TryGetVariable(identifier, out slot)) { if (!createIfMissing) { return(-1); } var variableType = symbol.GetTypeOrReturnType().Type; if (!forceSlotEvenIfEmpty && IsEmptyStructType(variableType)) { return(-1); } slot = AddVariable(identifier); } if (IsConditionalState) { Normalize(ref this.StateWhenTrue); Normalize(ref this.StateWhenFalse); } else { Normalize(ref this.State); } return(slot); }
/// <summary> /// Force a variable to have a slot. Returns -1 if the variable has an empty struct type. /// </summary> protected int GetOrCreateSlot(Symbol symbol, int containingSlot = 0, bool forceSlotEvenIfEmpty = false) { Debug.Assert(containingSlot >= 0); if (symbol.Kind == SymbolKind.RangeVariable) { return(-1); } containingSlot = DescendThroughTupleRestFields(ref symbol, containingSlot, forceContainingSlotsToExist: true); if (containingSlot < 0) { // Error case. Diagnostics should already have been produced. return(-1); } VariableIdentifier identifier = new VariableIdentifier(symbol, containingSlot); int slot; // Since analysis may proceed in multiple passes, it is possible the slot is already assigned. if (!_variableSlot.TryGetValue(identifier, out slot)) { var variableType = symbol.GetTypeOrReturnType().Type; if (!forceSlotEvenIfEmpty && IsEmptyStructType(variableType)) { return(-1); } if (_maxSlotDepth > 0 && GetSlotDepth(containingSlot) >= _maxSlotDepth) { return(-1); } slot = nextVariableSlot++; _variableSlot.Add(identifier, slot); if (slot >= variableBySlot.Length) { Array.Resize(ref this.variableBySlot, slot * 2); } variableBySlot[slot] = identifier; } if (IsConditionalState) { Normalize(ref this.StateWhenTrue); Normalize(ref this.StateWhenFalse); } else { Normalize(ref this.State); } return(slot); }
/// <summary> /// Force a variable to have a slot. Returns -1 if the variable has an empty struct type. /// </summary> protected virtual int GetOrCreateSlot(Symbol symbol, int containingSlot = 0, bool forceSlotEvenIfEmpty = false) { if (symbol.Kind == SymbolKind.RangeVariable) { return(-1); } containingSlot = DescendThroughTupleRestFields(ref symbol, containingSlot, forceContainingSlotsToExist: true); VariableIdentifier identifier = new VariableIdentifier(symbol, containingSlot); int slot; // Since analysis may proceed in multiple passes, it is possible the slot is already assigned. if (!_variableSlot.TryGetValue(identifier, out slot)) { var variableType = symbol.GetTypeOrReturnType().Type; if (!forceSlotEvenIfEmpty && _emptyStructTypeCache.IsEmptyStructType(variableType)) { return(-1); } slot = nextVariableSlot++; _variableSlot.Add(identifier, slot); if (slot >= variableBySlot.Length) { Array.Resize(ref this.variableBySlot, slot * 2); } variableBySlot[slot] = identifier; } if (IsConditionalState) { Normalize(ref this.StateWhenTrue); Normalize(ref this.StateWhenFalse); } else { Normalize(ref this.State); } return(slot); }
/// <summary> /// Learn from any constant null patterns appearing in the pattern. /// </summary> /// <param name="inputType">Type type of the input expression (before nullable analysis). /// Used to determine which types can contain null.</param> /// <returns>true if there is a top-level explicit null check</returns> private void LearnFromAnyNullPatterns( int inputSlot, TypeSymbol inputType, BoundPattern pattern) { if (inputSlot <= 0) { return; } // https://github.com/dotnet/roslyn/issues/35041 We only need to do this when we're rewriting, so we // can get information for any nodes in the pattern. VisitPatternForRewriting(pattern); switch (pattern) { case BoundConstantPattern cp: bool isExplicitNullCheck = cp.Value.ConstantValue == ConstantValue.Null; if (isExplicitNullCheck) { LearnFromNullTest(inputSlot, inputType, ref this.State); } break; case BoundDeclarationPattern _: case BoundDiscardPattern _: case BoundITuplePattern _: break; // nothing to learn case BoundRecursivePattern rp: { // for positional part: we only learn from tuples (not Deconstruct) if (rp.DeconstructMethod is null && !rp.Deconstruction.IsDefault) { var elements = inputType.TupleElements; for (int i = 0, n = Math.Min(rp.Deconstruction.Length, elements.IsDefault ? 0 : elements.Length); i < n; i++) { BoundSubpattern item = rp.Deconstruction[i]; FieldSymbol element = elements[i]; LearnFromAnyNullPatterns(GetOrCreateSlot(element, inputSlot), element.Type, item.Pattern); } } // for property part if (!rp.Properties.IsDefault) { for (int i = 0, n = rp.Properties.Length; i < n; i++) { BoundSubpattern item = rp.Properties[i]; Symbol symbol = item.Symbol; if (symbol?.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions) == true) { LearnFromAnyNullPatterns(GetOrCreateSlot(symbol, inputSlot), symbol.GetTypeOrReturnType().Type, item.Pattern); } } } } break; default: throw ExceptionUtilities.UnexpectedValue(pattern); } }
/// <summary> /// Performs the following checks: /// /// Spec 7.6.5: Invocation expressions (definition of Final Validation) /// The method is validated in the context of the method group: If the best method is a static method, /// the method group must have resulted from a simple-name or a member-access through a type. If the best /// method is an instance method, the method group must have resulted from a simple-name, a member-access /// through a variable or value, or a base-access. If neither of these requirements is true, a binding-time /// error occurs. /// (Note that the spec omits to mention, in the case of an instance method invoked through a simple name, that /// the invocation must appear within the body of an instance method) /// /// Spec 7.5.4: Compile-time checking of dynamic overload resolution /// If F is a static method, the method group must have resulted from a simple-name, a member-access through a type, /// or a member-access whose receiver can’t be classified as a type or value until after overload resolution (see §7.6.4.1). /// If F is an instance method, the method group must have resulted from a simple-name, a member-access through a variable or value, /// or a member-access whose receiver can’t be classified as a type or value until after overload resolution (see §7.6.4.1). /// </summary> /// <returns> /// True if there is any error. /// </returns> private bool MemberGroupFinalValidationAccessibilityChecks(BoundExpression receiverOpt, Symbol memberSymbol, CSharpSyntaxNode node, DiagnosticBag diagnostics, bool invokedAsExtensionMethod) { // Perform final validation of the method to be invoked. Debug.Assert(memberSymbol.Kind != SymbolKind.Method || memberSymbol.CanBeReferencedByName); //should be true since the caller has LookupOptions.MustBeReferenceableByName set //note that the same assert does not hold for all properties. Some properties and (all indexers) are not referenceable by name, yet //their binding brings them through here, perhaps needlessly. if (receiverOpt != null && receiverOpt.Kind == BoundKind.TypeOrValueExpression) { // TypeOrValue expression isn't replaced only if the invocation is late bound, in which case it can't be extension method. // None of the checks below apply if the receiver can’t be classified as a type or value. Debug.Assert(!invokedAsExtensionMethod); } else if (memberSymbol.IsStatic) { Debug.Assert(!invokedAsExtensionMethod || (receiverOpt != null)); if (!invokedAsExtensionMethod && !WasImplicitReceiver(receiverOpt) && IsMemberAccessedThroughVariableOrValue(receiverOpt)) { if (this.Flags.Includes(BinderFlags.CollectionInitializerAddMethod)) { diagnostics.Add(ErrorCode.ERR_InitializerAddHasWrongSignature, node.Location, memberSymbol); } else if (node.Kind == SyntaxKind.AwaitExpression && memberSymbol.Name == WellKnownMemberNames.GetAwaiter) { diagnostics.Add(ErrorCode.ERR_BadAwaitArg, node.Location, receiverOpt.Type); } else { diagnostics.Add(ErrorCode.ERR_ObjectProhibited, node.Location, memberSymbol); } return true; } } else if (IsMemberAccessedThroughType(receiverOpt)) { diagnostics.Add(ErrorCode.ERR_ObjectRequired, node.Location, memberSymbol); return true; } else if (WasImplicitReceiver(receiverOpt)) { if (InFieldInitializer && !ContainingType.IsScriptClass || InConstructorInitializer || InAttributeArgument) { CSharpSyntaxNode errorNode = node; if (node.Parent != null && node.Parent.Kind == SyntaxKind.InvocationExpression) { errorNode = node.Parent; } ErrorCode code = InFieldInitializer ? ErrorCode.ERR_FieldInitRefNonstatic : ErrorCode.ERR_ObjectRequired; diagnostics.Add(code, errorNode.Location, memberSymbol); return true; } // If we could access the member thru implicit "this" the receiver would be a BoundThisReference. // If it is null it means that the instance member is inaccessible. if (receiverOpt == null || ContainingMember().IsStatic) { Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, memberSymbol); return true; } } var containingType = this.ContainingType; if ((object)containingType != null) { HashSet<DiagnosticInfo> useSiteDiagnostics = null; bool isAccessible = this.IsSymbolAccessibleConditional(memberSymbol.GetTypeOrReturnType(), containingType, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); if (!isAccessible) { // In the presence of non-transitive [InternalsVisibleTo] in source, or obnoxious symbols from metadata, it is possible // to select a method through overload resolution in which the type is not accessible. In this case a method cannot // be called through normal IL, so we give an error. Neither [InternalsVisibleTo] nor the need for this diagnostic is // described by the language specification. // // Dev11 perform different access checks. See bug #530360 and tests AccessCheckTests.InaccessibleReturnType. Error(diagnostics, ErrorCode.ERR_BadAccess, node, memberSymbol); return true; } } return false; }