protected BindingContext(BindingContext parent) { m_ExprFactory = parent.m_ExprFactory; m_outputContext = parent.m_outputContext; m_pNameGenerator = parent.m_pNameGenerator; m_pInputFile = parent.m_pInputFile; m_pParentDecl = parent.m_pParentDecl; m_pContainingAgg = parent.m_pContainingAgg; m_pCurrentSwitchType = parent.m_pCurrentSwitchType; m_pOriginalConstantField = parent.m_pOriginalConstantField; m_pCurrentFieldSymbol = parent.m_pCurrentFieldSymbol; m_pImplicitlyTypedLocal = parent.m_pImplicitlyTypedLocal; m_pOuterScope = parent.m_pOuterScope; m_pFinallyScope = parent.m_pFinallyScope; m_pTryScope = parent.m_pTryScope; m_pCatchScope = parent.m_pCatchScope; m_pCurrentScope = parent.m_pCurrentScope; m_pSwitchScope = parent.m_pSwitchScope; m_pCurrentBlock = parent.m_pCurrentBlock; m_ppamis = parent.m_ppamis; m_pamiCurrent = parent.m_pamiCurrent; m_UnsafeState = parent.m_UnsafeState; m_FinallyNestingCount = parent.m_FinallyNestingCount; m_bInsideTryOfCatch = parent.m_bInsideTryOfCatch; m_bInFieldInitializer = parent.m_bInFieldInitializer; m_bInBaseConstructorCall = parent.m_bInBaseConstructorCall; CheckedNormal = parent.CheckedNormal; CheckedConstant = parent.CheckedConstant; m_aidExternAliasLookupContext = parent.m_aidExternAliasLookupContext; m_bAllowUnsafeBlocks = parent.m_bAllowUnsafeBlocks; m_bIsOptimizingSwitchAndArrayInit = parent.m_bIsOptimizingSwitchAndArrayInit; m_bShowReachability = parent.m_bShowReachability; m_bWrapNonExceptionThrows = parent.m_bWrapNonExceptionThrows; m_bflushLocalVariableTypesForEachStatement = parent.m_bflushLocalVariableTypesForEachStatement; m_bInRefactoring = parent.m_bInRefactoring; m_bInAttribute = parent.m_bInAttribute; m_bRespectSemanticsAndReportErrors = parent.m_bRespectSemanticsAndReportErrors; m_pInitType = parent.m_pInitType; m_returnErrorSink = parent.m_returnErrorSink; Debug.Assert(parent.SemanticChecker != null); this.SemanticChecker = parent.SemanticChecker; this.SymbolLoader = SemanticChecker.GetSymbolLoader(); }
private bool TryVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, AggregateType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); Debug.Assert(typeSrc.isInterfaceType() || typeSrc.isDelegateType()); typeDst = null; AggregateSymbol aggSym = typeSrc.GetOwningAggregate(); AggregateType aggOpenType = aggSym.getThisType(); if (!semanticChecker.CheckTypeAccess(aggOpenType, bindingContext.ContextForMemberLookup())) { // if the aggregate symbol itself is not accessible, then forget it, there is no // variance that will help us arrive at an accessible type. return false; } TypeArray typeArgs = typeSrc.GetTypeArgsThis(); TypeArray typeParams = aggOpenType.GetTypeArgsThis(); CType[] newTypeArgsTemp = new CType[typeArgs.size]; for (int i = 0; i < typeArgs.size; i++) { if (semanticChecker.CheckTypeAccess(typeArgs.Item(i), bindingContext.ContextForMemberLookup())) { // we have an accessible argument, this position is not a problem. newTypeArgsTemp[i] = typeArgs.Item(i); continue; } if (!typeArgs.Item(i).IsRefType() || !typeParams.Item(i).AsTypeParameterType().Covariant) { // This guy is inaccessible, and we are not going to be able to vary him, so we need to fail. return false; } CType intermediateTypeArg; if (GetBestAccessibleType(semanticChecker, bindingContext, typeArgs.Item(i), out intermediateTypeArg)) { // now we either have a value type (which must be accessible due to the above // check, OR we have an inaccessible type (which must be a ref type). In either // case, the recursion worked out and we are OK to vary this argument. newTypeArgsTemp[i] = intermediateTypeArg; continue; } else { Debug.Assert(false, "GetBestAccessibleType unexpectedly failed on a type that was used as a type parameter"); return false; } } TypeArray newTypeArgs = semanticChecker.getBSymmgr().AllocParams(typeArgs.size, newTypeArgsTemp); CType intermediateType = this.GetAggregate(aggSym, typeSrc.outerType, newTypeArgs); // All type arguments were varied successfully, which means now we must be accessible. But we could // have violated constraints. Let's check that out. if (!TypeBind.CheckConstraints(semanticChecker, null/*ErrorHandling*/, intermediateType, CheckConstraintsFlags.NoErrors)) { return false; } typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; }
private bool TryArrayVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, ArrayType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); typeDst = null; // We are here because we have an array type with an inaccessible element type. If possible, // we should create a new array type that has an accessible element type for which a // conversion exists. CType elementType = typeSrc.GetElementType(); if (!elementType.IsRefType()) { // Covariant array conversions exist for reference types only. return false; } CType intermediateType; if (GetBestAccessibleType(semanticChecker, bindingContext, elementType, out intermediateType)) { typeDst = this.GetArray(intermediateType, typeSrc.rank); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } return false; }
private void Reset() { _controller = new RuntimeBinderController(); _semanticChecker = new LangCompiler(_controller, new NameManager()); BSYMMGR bsymmgr = _semanticChecker.getBSymmgr(); NameManager nameManager = _semanticChecker.GetNameManager(); InputFile infile = bsymmgr.GetMiscSymFactory().CreateMDInfile(nameManager.Lookup(""), (mdToken)0); infile.SetAssemblyID(bsymmgr.AidAlloc(infile)); infile.AddToAlias(KAID.kaidThisAssembly); infile.AddToAlias(KAID.kaidGlobal); _symbolTable = new SymbolTable( bsymmgr.GetSymbolTable(), bsymmgr.GetSymFactory(), nameManager, _semanticChecker.GetTypeManager(), bsymmgr, _semanticChecker, infile); _semanticChecker.getPredefTypes().Init(_semanticChecker.GetErrorContext(), _symbolTable); _semanticChecker.GetTypeManager().InitTypeFactory(_symbolTable); SymbolLoader.getPredefinedMembers().RuntimeBinderSymbolTable = _symbolTable; SymbolLoader.SetSymbolTable(_symbolTable); _exprFactory = new ExprFactory(_semanticChecker.GetSymbolLoader().GetGlobalSymbolContext()); _outputContext = new OutputContext(); _nameGenerator = new NameGenerator(); _bindingContext = BindingContext.CreateInstance( _semanticChecker, _exprFactory, _outputContext, _nameGenerator, false, true, false, false, false, false, 0); _binder = new ExpressionBinder(_bindingContext); }
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! internal bool GetBestAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, CType typeSrc, out CType typeDst) { // This method implements the "best accessible type" algorithm for determining the type // of untyped arguments in the runtime binder. It is also used in method type inference // to fix type arguments to types that are accessible. // The new type is returned in an out parameter. The result will be true (and the out param // non-null) only when the algorithm could find a suitable accessible type. Debug.Assert(semanticChecker != null); Debug.Assert(bindingContext != null); Debug.Assert(typeSrc != null); typeDst = null; if (semanticChecker.CheckTypeAccess(typeSrc, bindingContext.ContextForMemberLookup())) { // If we already have an accessible type, then use it. This is the terminal point of the recursion. typeDst = typeSrc; return true; } // These guys have no accessibility concerns. Debug.Assert(!typeSrc.IsVoidType() && !typeSrc.IsErrorType() && !typeSrc.IsTypeParameterType()); if (typeSrc.IsParameterModifierType() || typeSrc.IsPointerType()) { // We cannot vary these. return false; } CType intermediateType; if ((typeSrc.isInterfaceType() || typeSrc.isDelegateType()) && TryVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, typeSrc.AsAggregateType(), out intermediateType)) { // If we have an interface or delegate type, then it can potentially be varied by its type arguments // to produce an accessible type, and if that's the case, then return that. // Example: IEnumerable<PrivateConcreteFoo> --> IEnumerable<PublicAbstractFoo> typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } if (typeSrc.IsArrayType() && TryArrayVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, typeSrc.AsArrayType(), out intermediateType)) { // Similarly to the interface and delegate case, arrays are covariant in their element type and // so we can potentially produce an array type that is accessible. // Example: PrivateConcreteFoo[] --> PublicAbstractFoo[] typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } if (typeSrc.IsNullableType()) { // We have an inaccessible nullable type, which means that the best we can do is System.ValueType. typeDst = this.GetOptPredefAgg(PredefinedType.PT_VALUE).getThisType(); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } if (typeSrc.IsArrayType()) { // We have an inaccessible array type for which we could not earlier find a better array type // with a covariant conversion, so the best we can do is System.Array. typeDst = this.GetReqPredefAgg(PredefinedType.PT_ARRAY).getThisType(); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup())); return true; } Debug.Assert(typeSrc.IsAggregateType()); if (typeSrc.IsAggregateType()) { // We have an AggregateType, so recurse on its base class. AggregateType aggType = typeSrc.AsAggregateType(); AggregateType baseType = aggType.GetBaseClass(); if (baseType == null) { // This happens with interfaces, for instance. But in that case, the // conversion to object does exist, is an implicit reference conversion, // and so we will use it. baseType = this.GetReqPredefAgg(PredefinedType.PT_OBJECT).getThisType(); } return GetBestAccessibleType(semanticChecker, bindingContext, baseType, out typeDst); } return false; }
public ExpressionBinder(BindingContext context) { Context = context; m_nullable = new CNullable(GetSymbolLoader(), GetErrorContext(), GetExprFactory()); g_binopSignatures = new BinOpSig[] { new BinOpSig (PredefinedType.PT_INT, PredefinedType.PT_INT, BinOpMask.Integer, 8, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), new BinOpSig (PredefinedType.PT_UINT, PredefinedType.PT_UINT, BinOpMask.Integer, 7, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), new BinOpSig (PredefinedType.PT_LONG, PredefinedType.PT_LONG, BinOpMask.Integer, 6, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), new BinOpSig (PredefinedType.PT_ULONG, PredefinedType.PT_ULONG, BinOpMask.Integer, 5, BindIntBinOp, OpSigFlags.Value, BinOpFuncKind.IntBinOp ), /* ERROR */ new BinOpSig (PredefinedType.PT_ULONG, PredefinedType.PT_LONG, BinOpMask.Integer, 4, null, OpSigFlags.Value, BinOpFuncKind.None ), /* ERROR */ new BinOpSig (PredefinedType.PT_LONG, PredefinedType.PT_ULONG, BinOpMask.Integer, 3, null, OpSigFlags.Value, BinOpFuncKind.None ), new BinOpSig (PredefinedType.PT_FLOAT, PredefinedType.PT_FLOAT, BinOpMask.Real, 1, BindRealBinOp, OpSigFlags.Value, BinOpFuncKind.RealBinOp ), new BinOpSig (PredefinedType.PT_DOUBLE, PredefinedType.PT_DOUBLE, BinOpMask.Real, 0, BindRealBinOp, OpSigFlags.Value, BinOpFuncKind.RealBinOp ), new BinOpSig (PredefinedType.PT_DECIMAL, PredefinedType.PT_DECIMAL, BinOpMask.Real, 0, BindDecBinOp, OpSigFlags.Value, BinOpFuncKind.DecBinOp ), new BinOpSig (PredefinedType.PT_STRING, PredefinedType.PT_STRING, BinOpMask.Equal, 0, BindStrCmpOp, OpSigFlags.Reference, BinOpFuncKind.StrCmpOp ), new BinOpSig (PredefinedType.PT_STRING, PredefinedType.PT_STRING, BinOpMask.Add, 2, BindStrBinOp, OpSigFlags.Reference, BinOpFuncKind.StrBinOp ), new BinOpSig (PredefinedType.PT_STRING, PredefinedType.PT_OBJECT, BinOpMask.Add, 1, BindStrBinOp, OpSigFlags.Reference, BinOpFuncKind.StrBinOp ), new BinOpSig (PredefinedType.PT_OBJECT, PredefinedType.PT_STRING, BinOpMask.Add, 0, BindStrBinOp, OpSigFlags.Reference, BinOpFuncKind.StrBinOp ), new BinOpSig (PredefinedType.PT_INT, PredefinedType.PT_INT, BinOpMask.Shift, 3, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), new BinOpSig (PredefinedType.PT_UINT, PredefinedType.PT_INT, BinOpMask.Shift, 2, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), new BinOpSig (PredefinedType.PT_LONG, PredefinedType.PT_INT, BinOpMask.Shift, 1, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), new BinOpSig (PredefinedType.PT_ULONG, PredefinedType.PT_INT, BinOpMask.Shift, 0, BindShiftOp, OpSigFlags.Value, BinOpFuncKind.ShiftOp ), new BinOpSig (PredefinedType.PT_BOOL, PredefinedType.PT_BOOL, BinOpMask.BoolNorm, 0, BindBoolBinOp, OpSigFlags.Value, BinOpFuncKind.BoolBinOp ), // Make boolean logical operators liftable so that they don't give funny short circuiting semantics. // This is for DDBugs 677075. new BinOpSig (PredefinedType.PT_BOOL, PredefinedType.PT_BOOL, BinOpMask.Logical, 0, BindBoolBinOp, OpSigFlags.BoolBit, BinOpFuncKind.BoolBinOp ), new BinOpSig (PredefinedType.PT_BOOL, PredefinedType.PT_BOOL, BinOpMask.Bitwise, 0, BindLiftedBoolBitwiseOp, OpSigFlags.BoolBit, BinOpFuncKind.BoolBitwiseOp ), }; g_rguos = new UnaOpSig[] { new UnaOpSig( PredefinedType.PT_INT, UnaOpMask.Signed, 7, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), new UnaOpSig( PredefinedType.PT_UINT, UnaOpMask.Unsigned, 6, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), new UnaOpSig( PredefinedType.PT_LONG, UnaOpMask.Signed, 5, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), new UnaOpSig( PredefinedType.PT_ULONG, UnaOpMask.Unsigned, 4, BindIntUnaOp, UnaOpFuncKind.IntUnaOp ), /* ERROR */ new UnaOpSig( PredefinedType.PT_ULONG, UnaOpMask.Minus, 3, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_FLOAT, UnaOpMask.Real, 1, BindRealUnaOp, UnaOpFuncKind.RealUnaOp ), new UnaOpSig( PredefinedType.PT_DOUBLE, UnaOpMask.Real, 0, BindRealUnaOp, UnaOpFuncKind.RealUnaOp ), new UnaOpSig( PredefinedType.PT_DECIMAL, UnaOpMask.Real, 0, BindDecUnaOp, UnaOpFuncKind.DecUnaOp ), new UnaOpSig( PredefinedType.PT_BOOL, UnaOpMask.Bool, 0, BindBoolUnaOp, UnaOpFuncKind.BoolUnaOp ), new UnaOpSig( PredefinedType.PT_INT, UnaOpMask.IncDec, 6, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_UINT, UnaOpMask.IncDec, 5, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_LONG, UnaOpMask.IncDec, 4, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_ULONG, UnaOpMask.IncDec, 3, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_FLOAT, UnaOpMask.IncDec, 1, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_DOUBLE, UnaOpMask.IncDec, 0, null, UnaOpFuncKind.None ), new UnaOpSig( PredefinedType.PT_DECIMAL, UnaOpMask.IncDec, 0, null, UnaOpFuncKind.None ), }; }
public static void RecordUnsafeUsage(BindingContext context) { if (!(context.GetUnsafeState() == UNSAFESTATES.UNSAFESTATES_Unsafe) && !context.GetOutputContext().m_bUnsafeErrorGiven) { context.GetOutputContext().m_bUnsafeErrorGiven = true; } }
private bool TryArrayVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, ArrayType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); typeDst = null; // We are here because we have an array type with an inaccessible element type. If possible, // we should create a new array type that has an accessible element type for which a // conversion exists. CType elementType = typeSrc.GetElementType(); if (!elementType.IsRefType()) { // Covariant array conversions exist for reference types only. return(false); } CType intermediateType; if (GetBestAccessibleType(semanticChecker, bindingContext, elementType, out intermediateType)) { typeDst = this.GetArray(intermediateType, typeSrc.rank, typeSrc.IsSZArray); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); } return(false); }
private bool TryVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, AggregateType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); Debug.Assert(typeSrc.isInterfaceType() || typeSrc.isDelegateType()); typeDst = null; AggregateSymbol aggSym = typeSrc.GetOwningAggregate(); AggregateType aggOpenType = aggSym.getThisType(); if (!semanticChecker.CheckTypeAccess(aggOpenType, bindingContext.ContextForMemberLookup)) { // if the aggregate symbol itself is not accessible, then forget it, there is no // variance that will help us arrive at an accessible type. return(false); } TypeArray typeArgs = typeSrc.GetTypeArgsThis(); TypeArray typeParams = aggOpenType.GetTypeArgsThis(); CType[] newTypeArgsTemp = new CType[typeArgs.Count]; for (int i = 0; i < typeArgs.Count; i++) { if (semanticChecker.CheckTypeAccess(typeArgs[i], bindingContext.ContextForMemberLookup)) { // we have an accessible argument, this position is not a problem. newTypeArgsTemp[i] = typeArgs[i]; continue; } if (!typeArgs[i].IsRefType() || !((TypeParameterType)typeParams[i]).Covariant) { // This guy is inaccessible, and we are not going to be able to vary him, so we need to fail. return(false); } CType intermediateTypeArg; if (GetBestAccessibleType(semanticChecker, bindingContext, typeArgs[i], out intermediateTypeArg)) { // now we either have a value type (which must be accessible due to the above // check, OR we have an inaccessible type (which must be a ref type). In either // case, the recursion worked out and we are OK to vary this argument. newTypeArgsTemp[i] = intermediateTypeArg; continue; } else { Debug.Assert(false, "GetBestAccessibleType unexpectedly failed on a type that was used as a type parameter"); return(false); } } TypeArray newTypeArgs = semanticChecker.getBSymmgr().AllocParams(typeArgs.Count, newTypeArgsTemp); CType intermediateType = this.GetAggregate(aggSym, typeSrc.outerType, newTypeArgs); // All type arguments were varied successfully, which means now we must be accessible. But we could // have violated constraints. Let's check that out. if (!TypeBind.CheckConstraints(semanticChecker, null /*ErrorHandling*/, intermediateType, CheckConstraintsFlags.NoErrors)) { return(false); } typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); }
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! internal bool GetBestAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, CType typeSrc, out CType typeDst) { // This method implements the "best accessible type" algorithm for determining the type // of untyped arguments in the runtime binder. It is also used in method type inference // to fix type arguments to types that are accessible. // The new type is returned in an out parameter. The result will be true (and the out param // non-null) only when the algorithm could find a suitable accessible type. Debug.Assert(semanticChecker != null); Debug.Assert(bindingContext != null); Debug.Assert(typeSrc != null); typeDst = null; if (semanticChecker.CheckTypeAccess(typeSrc, bindingContext.ContextForMemberLookup)) { // If we already have an accessible type, then use it. This is the terminal point of the recursion. typeDst = typeSrc; return(true); } // These guys have no accessibility concerns. Debug.Assert(!(typeSrc is VoidType) && !(typeSrc is ErrorType) && !(typeSrc is TypeParameterType)); if (typeSrc is ParameterModifierType || typeSrc is PointerType) { // We cannot vary these. return(false); } CType intermediateType; if (typeSrc is AggregateType aggSrc && (aggSrc.isInterfaceType() || aggSrc.isDelegateType()) && TryVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, aggSrc, out intermediateType)) { // If we have an interface or delegate type, then it can potentially be varied by its type arguments // to produce an accessible type, and if that's the case, then return that. // Example: IEnumerable<PrivateConcreteFoo> --> IEnumerable<PublicAbstractFoo> typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); } if (typeSrc is ArrayType arrSrc && TryArrayVarianceAdjustmentToGetAccessibleType(semanticChecker, bindingContext, arrSrc, out intermediateType)) { // Similarly to the interface and delegate case, arrays are covariant in their element type and // so we can potentially produce an array type that is accessible. // Example: PrivateConcreteFoo[] --> PublicAbstractFoo[] typeDst = intermediateType; Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); } if (typeSrc is NullableType) { // We have an inaccessible nullable type, which means that the best we can do is System.ValueType. typeDst = GetPredefAgg(PredefinedType.PT_VALUE).getThisType(); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); } if (typeSrc is ArrayType) { // We have an inaccessible array type for which we could not earlier find a better array type // with a covariant conversion, so the best we can do is System.Array. typeDst = GetPredefAgg(PredefinedType.PT_ARRAY).getThisType(); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); } Debug.Assert(typeSrc is AggregateType); if (typeSrc is AggregateType aggType) { // We have an AggregateType, so recurse on its base class. AggregateType baseType = aggType.GetBaseClass(); if (baseType == null) { // This happens with interfaces, for instance. But in that case, the // conversion to object does exist, is an implicit reference conversion, // and so we will use it. baseType = GetPredefAgg(PredefinedType.PT_OBJECT).getThisType(); } return(GetBestAccessibleType(semanticChecker, bindingContext, baseType, out typeDst)); } return(false); }