### Summary

If the argument spec defines a dict variable as required:

- if the variable has no default in defaults/main.yml and is not provided in the playbook, argument spec validation will fail. That's OK.
- if the variable is defined as a string e.g. `''` or `'test-string'`, validation will fail. That's OK.
- if the variable is defines as None or `~`, validation will pass. That's NOT OK.

### Issue Type

Bug Report

### Component Name

argument_spec

### Ansible Version

```console
$ ansible --version
ansible [core 2.15.0.dev0] (devel 51bddd862b) last updated 2023/01/03 22:32:16 (GMT +000)
  config file = /home/nikos/projects/ansible-demo/ansible.cfg
  configured module search path = ['/home/nikos/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/nikos/projects/ansible/lib/ansible
  ansible collection location = /home/nikos/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/nikos/projects/ansible/bin/ansible
  python version = 3.10.8 (main, Nov  1 2022, 14:18:21) [GCC 12.2.0] (/usr/bin/python)
  jinja version = 3.1.2
  libyaml = True
```


### Configuration

```console
# if using a version older than ansible-core 2.12 you should omit the '-t all'
$ ansible-config dump --only-changed -t all
ANSIBLE_NOCOWS(/home/nikos/projects/ansible-demo/ansible.cfg) = True
CACHE_PLUGIN(/home/nikos/projects/ansible-demo/ansible.cfg) = ansible.builtin.json
CACHE_PLUGIN_CONNECTION(/home/nikos/projects/ansible-demo/ansible.cfg) = facts_cache
CONFIG_FILE() = /home/nikos/projects/ansible-demo/ansible.cfg
DEFAULT_EXECUTABLE(/home/nikos/projects/ansible-demo/ansible.cfg) = /bin/bash
DEFAULT_GATHERING(/home/nikos/projects/ansible-demo/ansible.cfg) = implicit
DEFAULT_HOST_LIST(/home/nikos/projects/ansible-demo/ansible.cfg) = ['/home/nikos/projects/ansible-demo/inventory.ini']
DEFAULT_MANAGED_STR(/home/nikos/projects/ansible-demo/ansible.cfg) = \nAnsible managed (do not edit, changes may be overwritten)
EDITOR(env: EDITOR) = nvim

CACHE:
=====

jsonfile:
________
```


### OS / Environment

Controller and target: Arch Linux

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
# role argument specs
---

argument_specs:
  main:
    options:
      dict_var:
        type: dict
        required: true
```

```
# playbook
---

- hosts: all
  roles:
    - myrole
  vars:
    dict_var: ~
```

### Expected Results

I expect argument validation to fail with a message that None type is not dict or could not be converted to dict.

### Actual Results

```console
Validation passes and role execution succeeds.
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
continue
                
                new_prefix = prefix + param
                if wanted == 'list':
                    new_prefix += '[%d]' % idx
                new_prefix += '.'
                alias_warnings = []
                alias_deprecations_sub = []
                try:
                    options_aliases = _handle_aliases(sub_spec, sub_parameters, alias_warnings, alias_deprecations_sub)
                except (TypeError, ValueError) as e:
                    options_aliases = {}
                    errors.append(AliasError(to_native(e)))
                for option, alias in alias_warnings:
                    warn('Both option %s%s and its alias %s%s are set.' % (new_prefix, option, new_prefix, alias))
                if alias_deprecations is not None:
                    for deprecation in alias_deprecations_sub:
                        alias_deprecations.append({
                            'name': '%s%s' % (new_prefix, deprecation['name']),
                            'version': deprecation.get('version'),
                            'date': deprecation.get('date'),
                            'collection_name': deprecation.get('collection_name'),
                        })
                try:
                    no_log_values.update(_list_no_log_values(sub_spec, sub_parameters))
                except TypeError as te:
                    errors.append(NoLogError(to_native(te)))
                legal_inputs = _get_legal_inputs(sub_spec, sub_parameters, options_aliases)
                unsupported_parameters.update(
                    _get_unsupported_parameters(