diff --git a/sol/inheritance.hpp b/sol/inheritance.hpp
index 120c36e5..3910ee92 100644
--- a/sol/inheritance.hpp
+++ b/sol/inheritance.hpp
@@ -67,6 +67,16 @@ namespace sol {
 			return key;
 		}
 
+		inline decltype(auto) base_class_index_propogation_key() {
+			static const auto& key = u8"\xF0\x9F\x8C\xB2.index";
+			return key;
+		}
+
+		inline decltype(auto) base_class_new_index_propogation_key() {
+			static const auto& key = u8"\xF0\x9F\x8C\xB2.new_index";
+			return key;
+		}
+
 		template <typename T, typename... Bases>
 		struct inheritance {
 			static bool type_check_bases(types<>, std::size_t) {
diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp
index 7ea66ddd..1f2a5692 100644
--- a/sol/usertype_metatable.hpp
+++ b/sol/usertype_metatable.hpp
@@ -138,11 +138,16 @@ namespace sol {
 		typedef std::tuple<clean_type_t<Tn> ...> Tuple;
 		template <std::size_t Idx>
 		struct check_binding : is_variable_binding<meta::unqualified_tuple_element_t<Idx, Tuple>> {};
+		typedef void (*base_walk)(lua_State*, bool&, int&, string_detail::string_shim&);
 		Tuple functions;
 		lua_CFunction indexfunc;
 		lua_CFunction newindexfunc;
 		lua_CFunction destructfunc;
 		lua_CFunction callconstructfunc;
+		lua_CFunction indexbase;
+		lua_CFunction newindexbase;
+		base_walk indexbaseclasspropogation;
+		base_walk newindexbaseclasspropogation;
 		void* baseclasscheck;
 		void* baseclasscast;
 		bool mustindex;
@@ -194,6 +199,8 @@ namespace sol {
 			static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report.");
 			baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check;
 			baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast;
+			indexbaseclasspropogation = walk_all_bases<true, Bases...>;
+			newindexbaseclasspropogation = walk_all_bases<false, Bases...>;
 		}
 
 		template <std::size_t Idx, typename N, typename F, typename = std::enable_if_t<!meta::any_same<meta::unqualified_t<N>, base_classes_tag, call_construction>::value>>
@@ -226,7 +233,10 @@ namespace sol {
 		template <typename... Args, typename = std::enable_if_t<sizeof...(Args) == sizeof...(Tn)>>
 		usertype_metatable(Args&&... args) : functions(std::forward<Args>(args)...),
 		indexfunc(usertype_detail::indexing_fail<true>), newindexfunc(usertype_detail::indexing_fail<false>),
-		destructfunc(nullptr), callconstructfunc(nullptr), baseclasscheck(nullptr), baseclasscast(nullptr), 
+		destructfunc(nullptr), callconstructfunc(nullptr), 
+		indexbase(&core_indexing_call<true>), newindexbase(&core_indexing_call<false>),
+		indexbaseclasspropogation(walk_all_bases<true>), newindexbaseclasspropogation(walk_all_bases<false>),
+		baseclasscheck(nullptr), baseclasscast(nullptr), 
 		mustindex(contains_variable() || contains_index()), secondarymeta(contains_variable()) {
 		}
 
@@ -251,32 +261,72 @@ namespace sol {
 			ret = real_find_call<I0, I1>(idx, L);
 		}
 
-		static int real_index_call(lua_State* L) {
-			usertype_metatable& f = stack::get<light<usertype_metatable>>(L, upvalue_index(1));
-			if (stack::get<type>(L, -1) == type::string) {
-				string_detail::string_shim accessor = stack::get<string_detail::string_shim>(L, -1);
-				bool found = false;
-				int ret = 0;
-				(void)detail::swallow{ 0, (f.find_call<I * 2, I * 2 + 1>(std::true_type(), L, found, ret, accessor), 0)... };
-				if (found) {
-					return ret;
-				}
+		template <bool b>
+		void propogating_call(lua_State* L, bool& found, int& ret, string_detail::string_shim& accessor) {
+			(void)detail::swallow{ 0, (find_call<I * 2, I * 2 + 1>(std::integral_constant<bool, b>(), L, found, ret, accessor), 0)... };
+		}
+
+		template <bool b, typename Base>
+		static void walk_single_base(lua_State* L, bool& found, int& ret, string_detail::string_shim&) {
+			if (found)
+				return;
+			const char* metakey = &usertype_traits<Base>::metatable[0];
+			const char* gcmetakey = &usertype_traits<Base>::gc_table[0];
+			const char* basewalkkey = b ? detail::base_class_index_propogation_key() : detail::base_class_new_index_propogation_key();
+			
+			luaL_getmetatable(L, metakey);
+			if (type_of(L, -1) == type::nil) {
+				lua_pop(L, 1);
+				return;
 			}
-			return f.indexfunc(L);
+			stack::get_field(L, basewalkkey);
+			if (type_of(L, -1) == type::nil) {
+				lua_pop(L, 2);
+				return;
+			}
+			lua_CFunction basewalkfunc = stack::pop<lua_CFunction>(L);
+			lua_pop(L, 1);
+			
+			stack::get_field<true>(L, gcmetakey);
+			int value = basewalkfunc(L);
+			if (value > -1) {
+				found = true;
+				ret = value;
+			}
+		}
+
+		template <bool b, typename... Bases>
+		static void walk_all_bases(lua_State* L, bool& found, int& ret, string_detail::string_shim& accessor) {
+			(void)detail::swallow{ 0, (walk_single_base<b, Bases>(L, found, ret, accessor), 0)... };
+		}
+
+		template <bool b, bool toplevel = false>
+		static int core_indexing_call(lua_State* L) {
+			usertype_metatable& f = toplevel ? stack::get<light<usertype_metatable>>(L, upvalue_index(1)) : stack::pop<light<usertype_metatable>>(L);
+			if (toplevel && stack::get<type>(L, -1) != type::string) {
+				return b ? f.indexfunc(L) : f.newindexfunc(L);
+			}
+			string_detail::string_shim accessor = stack::get<string_detail::string_shim>(L, -1);
+			int ret = 0;
+			bool found = false;
+			f.propogating_call<b>(L, found, ret, accessor);
+			if (found) {
+				return ret;
+			}
+			// Otherwise, we need to do propagating calls through the bases
+			f.indexbaseclasspropogation(L, found, ret, accessor);
+			if (found) {
+				return ret;
+			}
+			return toplevel ? (b ? f.indexfunc(L) : f.newindexfunc(L)) : -1;
+		}
+
+		static int real_index_call(lua_State* L) {
+			return core_indexing_call<true, true>(L);
 		}
 
 		static int real_new_index_call(lua_State* L) {
-			usertype_metatable& f = stack::get<light<usertype_metatable>>(L, upvalue_index(1));
-			if (stack::get<type>(L, -2) == type::string) {
-				string_detail::string_shim accessor = stack::get<string_detail::string_shim>(L, -2);
-				bool found = false;
-				int ret = 0;
-				(void)detail::swallow{ 0, (f.find_call<I * 2, I * 2 + 1>(std::false_type(), L, found, ret, accessor), 0)... };
-				if (found) {
-					return ret;
-				}
-			}
-			return f.newindexfunc(L);
+			return core_indexing_call<false, true>(L);
 		}
 
 		template <std::size_t Idx, bool is_index = true, bool is_variable = false>
@@ -394,6 +444,9 @@ namespace sol {
 					else {
 						stack::set_field(L, detail::base_class_cast_key(), nil, t.stack_index());
 					}
+					
+					stack::set_field(L, detail::base_class_index_propogation_key(), make_closure(um.indexbase, make_light(um)), t.stack_index());
+					stack::set_field(L, detail::base_class_new_index_propogation_key(), make_closure(um.newindexbase, make_light(um)), t.stack_index());
 
 					if (mustindex) {
 						// Basic index pushing: specialize