code: func completeAbstractValue(eCtx *executionContext, returnType Abstract, fieldASTs []*ast.Field, info ResolveInfo, path *ResponsePath, result interface{}) interface{} {
  
  	var runtimeType *Object
  
  	resolveTypeParams := ResolveTypeParams{
  		Value:   result,
  		Info:    info,
  		Context: eCtx.Context,
  	}
  	if unionReturnType, ok := returnType.(*Union); ok && unionReturnType.ResolveType != nil {
  		runtimeType = unionReturnType.ResolveType(resolveTypeParams)
  	} else if interfaceReturnType, ok := returnType.(*Interface); ok && interfaceReturnType.ResolveType != nil {
  		runtimeType = interfaceReturnType.ResolveType(resolveTypeParams)
  	} else {
  		runtimeType = defaultResolveTypeFn(resolveTypeParams, returnType)
  	}
  
  	err := invariant(runtimeType != nil,
  		fmt.Sprintf(`Abstract type %v must resolve to an Object type at runtime `+
  			`for field %v.%v with value "%v", received "%v".`,
  			returnType, info.ParentType, info.FieldName, result, runtimeType),
  	)
  	if err != nil {
  		panic(err)
  	}
  
  	if !eCtx.Schema.IsPossibleType(returnType, runtimeType) {
  		panic(gqlerrors.NewFormattedError(
  			fmt.Sprintf(`Runtime Object type "%v" is not a possible type `+
  				`for "%v".`, runtimeType, returnType),
  		))
  	}
  
  	return completeObjectValue(eCtx, runtimeType, fieldASTs, info, path, result)
  }
docs: // completeAbstractValue completes value of an Abstract type (Union / Interface) by determining the runtime type
  // of that value, then completing based on that type.