It’s plain pytorch.
Here is the function class that is repeated in the loop and output of the function at each iteration will be the input to the next iteration call:
class BertEncoder(nn.Module):
    def __init__(self, config):
        super(BertEncoder, self).__init__()
        self.output_attentions = config.output_attentions
        self.output_hidden_states = config.output_hidden_states
        #self.layer = nn.ModuleList([BertLayer(config) for _ in range(config.num_hidden_layers)])
        self.layer = nn.ModuleList([BertLayer(config) for _ in range(1)])

    def forward(self, hidden_states, attention_mask, head_mask=None, is_last_layer=False,
            mask_attention_probs_dropout_prob=None,
            mask_hidden_dropout_prob=None,
            layer=None):

        if is_last_layer == True:
            self.output_hidden_states = True
        all_hidden_states = ()
        all_attentions = ()
        for i, layer_module in enumerate(self.layer):
            if self.output_hidden_states:
                all_hidden_states = all_hidden_states + (hidden_states,)

            layer_outputs = layer_module(hidden_states, attention_mask, head_mask[i],
                                        mask_attention_probs_dropout_prob=mask_attention_probs_dropout_prob, 
                                        mask_hidden_dropout_prob=mask_hidden_dropout_prob,
                                        layer=layer)
            hidden_states = layer_outputs[0]

        return layer_outputs