private ILFunction CreateMemberFunction(ImplementNode implementBlock, FunctionNode func, ModulePage inContext, ILModule dstModule)
        {
            List <VarInfo> args = new List <VarInfo>();

            TypeInfo implementType;

            if (Context.TryGetType(inContext.Module.Name, $"{implementBlock.StructID}", out implementType))
            {
                args.Add(new VarInfo()
                {
                    Name = "this",
                    Type = new PointerTypeInfo(implementType)
                });
            }
            else if (Context.GlobalTypes.TryGetType($"{implementBlock.StructID}", out implementType))
            {
                args.Add(new VarInfo()
                {
                    Name = "this",
                    Type = new PointerTypeInfo(implementType)
                });
            }
            else
            {
                Errors.Add(new CompileError(implementBlock.StructID.Source, "Could not resolve type name"));
            }

            foreach (var param in func.Parameters)
            {
                var t = Context.GetType(param.Type, inContext) ?? Context.GlobalTypes.GetType("void");

                args.Add(new VarInfo()
                {
                    Name = $"{param.Identifier}",
                    Type = t
                });
            }

            // it seems a little silly, but we give the function a name that isn't actually a valid identifier
            // this makes the auto-generated function impossible to call outside of the intended context

            var    retType  = Context.GetType(func.Type, inContext) ?? Context.GlobalTypes.GetType("void");
            string funcName = $"::{inContext.Module.Name}.{implementBlock.StructID}.{func.Identifier}";

            inContext.Module.Constants.Add(funcName, new FuncRef()
            {
                InModule     = dstModule,
                FunctionName = funcName
            });
            return(dstModule.CreateFunction(funcName, args.ToArray(), retType));
        }