private static ACCESSERROR CheckAccessCore(Symbol symCheck, AggregateType atsCheck, Symbol symWhere, CType typeThru)
        {
            Debug.Assert(symCheck != null);
            Debug.Assert(atsCheck == null || symCheck.parent == atsCheck.OwningAggregate);
            Debug.Assert(typeThru == null ||
                         typeThru is AggregateType ||
                         typeThru is TypeParameterType ||
                         typeThru is ArrayType ||
                         typeThru is NullableType);

            switch (symCheck.GetAccess())
            {
            default:
                throw Error.InternalCompilerError();
            //return ACCESSERROR.ACCESSERROR_NOACCESS;

            case ACCESS.ACC_UNKNOWN:
                return(ACCESSERROR.ACCESSERROR_NOACCESS);

            case ACCESS.ACC_PUBLIC:
                return(ACCESSERROR.ACCESSERROR_NOERROR);

            case ACCESS.ACC_PRIVATE:
            case ACCESS.ACC_PROTECTED:
                if (symWhere == null)
                {
                    return(ACCESSERROR.ACCESSERROR_NOACCESS);
                }
                break;

            case ACCESS.ACC_INTERNAL:
            case ACCESS.ACC_INTERNALPROTECTED:       // Check internal, then protected.

                if (symWhere == null)
                {
                    return(ACCESSERROR.ACCESSERROR_NOACCESS);
                }
                if (symWhere.SameAssemOrFriend(symCheck))
                {
                    return(ACCESSERROR.ACCESSERROR_NOERROR);
                }
                if (symCheck.GetAccess() == ACCESS.ACC_INTERNAL)
                {
                    return(ACCESSERROR.ACCESSERROR_NOACCESS);
                }
                break;

            case ACCESS.ACC_INTERNAL_AND_PROTECTED:
                if (symWhere == null || !symWhere.SameAssemOrFriend(symCheck))
                {
                    return(ACCESSERROR.ACCESSERROR_NOACCESS);
                }

                break;
            }

            // Find the inner-most enclosing AggregateSymbol.
            AggregateSymbol aggWhere = null;

            for (Symbol symT = symWhere; symT != null; symT = symT.parent)
            {
                if (symT is AggregateSymbol aggSym)
                {
                    aggWhere = aggSym;
                    break;
                }
            }

            if (aggWhere == null)
            {
                return(ACCESSERROR.ACCESSERROR_NOACCESS);
            }

            // Should always have atsCheck for private and protected access check.
            // We currently don't need it since access doesn't respect instantiation.
            // We just use symWhere.parent as AggregateSymbol instead.
            AggregateSymbol aggCheck = symCheck.parent as AggregateSymbol;

            // First check for private access.
            for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg())
            {
                if (agg == aggCheck)
                {
                    return(ACCESSERROR.ACCESSERROR_NOERROR);
                }
            }

            if (symCheck.GetAccess() == ACCESS.ACC_PRIVATE)
            {
                return(ACCESSERROR.ACCESSERROR_NOACCESS);
            }

            // Handle the protected case - which is the only real complicated one.
            Debug.Assert(symCheck.GetAccess() == ACCESS.ACC_PROTECTED ||
                         symCheck.GetAccess() == ACCESS.ACC_INTERNALPROTECTED ||
                         symCheck.GetAccess() == ACCESS.ACC_INTERNAL_AND_PROTECTED);

            // Check if symCheck is in aggWhere or a base of aggWhere,
            // or in an outer agg of aggWhere or a base of an outer agg of aggWhere.

            AggregateType atsThru = null;

            if (typeThru != null && !symCheck.isStatic)
            {
                atsThru = typeThru.GetAts();
            }

            // Look for aggCheck among the base classes of aggWhere and outer aggs.
            bool found = false;

            for (AggregateSymbol agg = aggWhere; agg != null; agg = agg.GetOuterAgg())
            {
                Debug.Assert(agg != aggCheck); // We checked for this above.

                // Look for aggCheck among the base classes of agg.
                if (agg.FindBaseAgg(aggCheck))
                {
                    found = true;
                    // aggCheck is a base class of agg. Check atsThru.
                    // For non-static protected access to be legal, atsThru must be an instantiation of
                    // agg or a CType derived from an instantiation of agg. In this case
                    // all that matters is that agg is in the base AggregateSymbol chain of atsThru. The
                    // actual AGGTYPESYMs involved don't matter.
                    if (atsThru == null || atsThru.OwningAggregate.FindBaseAgg(agg))
                    {
                        return(ACCESSERROR.ACCESSERROR_NOERROR);
                    }
                }
            }

            // the CType in which the method is being called has no relationship with the
            // CType on which the method is defined surely this is NOACCESS and not NOACCESSTHRU
            return(found ? ACCESSERROR.ACCESSERROR_NOACCESSTHRU : ACCESSERROR.ACCESSERROR_NOACCESS);
        }