diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index b5c47f5cf..09c53eabf 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import contextlib, os.path, re, tempfile
+import collections.abc
 import typing
 
 from ..linkers import StaticLinker, GnuLikeDynamicLinkerMixin, SolarisDynamicLinker
@@ -385,11 +386,12 @@ class RunResult:
         self.stdout = stdout
         self.stderr = stderr
 
-class CompilerArgs(list):
+class CompilerArgs(typing.MutableSequence[str]):
     '''
-    Class derived from list() that manages a list of compiler arguments. Should
-    be used while constructing compiler arguments from various sources. Can be
-    operated with ordinary lists, so this does not need to be used everywhere.
+    List-like class that manages a list of compiler arguments. Should be used
+    while constructing compiler arguments from various sources. Can be
+    operated with ordinary lists, so this does not need to be used
+    everywhere.
 
     All arguments must be inserted and stored in GCC-style (-lfoo, -Idir, etc)
     and can converted to the native type of each compiler by using the
@@ -438,37 +440,45 @@ class CompilerArgs(list):
     # In generate_link() we add external libs without de-dup, but we must
     # *always* de-dup these because they're special arguments to the linker
     always_dedup_args = tuple('-l' + lib for lib in unixy_compiler_internal_libs)
-    compiler = None
-
-    def _check_args(self, args):
-        cargs = []
-        if len(args) > 2:
-            raise TypeError("CompilerArgs() only accepts at most 2 arguments: "
-                            "The compiler, and optionally an initial list")
-        elif not args:
-            return cargs
-        elif len(args) == 1:
-            if isinstance(args[0], (Compiler, StaticLinker)):
-                self.compiler = args[0]
-            else:
-                raise TypeError("you must pass a Compiler instance as one of "
-                                "the arguments")
-        elif len(args) == 2:
-            if isinstance(args[0], (Compiler, StaticLinker)):
-                self.compiler = args[0]
-                cargs = args[1]
-            elif isinstance(args[1], (Compiler, StaticLinker)):
-                cargs = args[0]
-                self.compiler = args[1]
-            else:
-                raise TypeError("you must pass a Compiler instance as one of "
-                                "the two arguments")
-        else:
-            raise AssertionError('Not reached')
-        return cargs
 
-    def __init__(self, *args):
-        super().__init__(self._check_args(args))
+    def __init__(self, compiler: typing.Union['Compiler', StaticLinker],
+                 iterable: typing.Optional[typing.Iterable[str]] = None):
+        self.compiler = compiler
+        self.__container = list(iterable) if iterable is not None else []  # type: typing.List[str]
+
+    @typing.overload
+    def __getitem__(self, index: int) -> str:
+        pass
+
+    @typing.overload
+    def __getitem__(self, index: slice) -> typing.List[str]:
+        pass
+
+    def __getitem__(self, index):
+        return self.__container[index]
+
+    @typing.overload
+    def __setitem__(self, index: int, value: str) -> None:
+        pass
+
+    @typing.overload
+    def __setitem__(self, index: slice, value: typing.List[str]) -> None:
+        pass
+
+    def __setitem__(self, index, value) -> None:
+        self.__container[index] = value
+
+    def __delitem__(self, index: typing.Union[int, slice]) -> None:
+        del self.__container[index]
+
+    def __len__(self) -> int:
+        return len(self.__container)
+
+    def insert(self, index: int, value: str) -> None:
+        self.__container.insert(index, value)
+
+    def copy(self) -> 'CompilerArgs':
+        return CompilerArgs(self.compiler, self.__container.copy())
 
     @classmethod
     def _can_dedup(cls, arg):
@@ -533,7 +543,7 @@ class CompilerArgs(list):
         # This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which
         # all act like (or are) gnu ld
         # TODO: this could probably be added to the DynamicLinker instead
-        if (hasattr(self.compiler, 'linker') and
+        if (isinstance(self.compiler, Compiler) and
                 self.compiler.linker is not None and
                 isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker))):
             group_start = -1
@@ -553,7 +563,7 @@ class CompilerArgs(list):
         # Remove system/default include paths added with -isystem
         if hasattr(self.compiler, 'get_default_include_dirs'):
             default_dirs = self.compiler.get_default_include_dirs()
-            bad_idx_list = []
+            bad_idx_list = []  # type: typing.List[int]
             for i, each in enumerate(new):
                 # Remove the -isystem and the path if the path is a default path
                 if (each == '-isystem' and
@@ -566,9 +576,9 @@ class CompilerArgs(list):
                     bad_idx_list += [i]
             for i in reversed(bad_idx_list):
                 new.pop(i)
-        return self.compiler.unix_args_to_native(new)
+        return self.compiler.unix_args_to_native(new.__container)
 
-    def append_direct(self, arg):
+    def append_direct(self, arg: str) -> None:
         '''
         Append the specified argument without any reordering or de-dup except
         for absolute paths to libraries, etc, which can always be de-duped
@@ -577,9 +587,9 @@ class CompilerArgs(list):
         if os.path.isabs(arg):
             self.append(arg)
         else:
-            super().append(arg)
+            self.__container.append(arg)
 
-    def extend_direct(self, iterable):
+    def extend_direct(self, iterable: typing.Iterable[str]) -> None:
         '''
         Extend using the elements in the specified iterable without any
         reordering or de-dup except for absolute paths where the order of
@@ -588,7 +598,7 @@ class CompilerArgs(list):
         for elem in iterable:
             self.append_direct(elem)
 
-    def extend_preserving_lflags(self, iterable):
+    def extend_preserving_lflags(self, iterable: typing.Iterable[str]) -> None:
         normal_flags = []
         lflags = []
         for i in iterable:
@@ -599,20 +609,20 @@ class CompilerArgs(list):
         self.extend(normal_flags)
         self.extend_direct(lflags)
 
-    def __add__(self, args):
-        new = CompilerArgs(self, self.compiler)
+    def __add__(self, args: typing.Iterable[str]) -> 'CompilerArgs':
+        new = self.copy()
         new += args
         return new
 
-    def __iadd__(self, args):
+    def __iadd__(self, args: typing.Iterable[str]) -> 'CompilerArgs':
         '''
         Add two CompilerArgs while taking into account overriding of arguments
         and while preserving the order of arguments as much as possible
         '''
-        pre = []
-        post = []
-        if not isinstance(args, list):
-            raise TypeError('can only concatenate list (not "{}") to list'.format(args))
+        pre = []   # type: typing.List[str]
+        post = []  # type: typing.List[str]
+        if not isinstance(args, collections.abc.Iterable):
+            raise TypeError('can only concatenate Iterable[str] (not "{}") to CompilerArgs'.format(args))
         for arg in args:
             # If the argument can be de-duped, do it either by removing the
             # previous occurrence of it and adding a new one, or not adding the
@@ -637,39 +647,31 @@ class CompilerArgs(list):
         # Insert at the beginning
         self[:0] = pre
         # Append to the end
-        super().__iadd__(post)
+        self.__container += post
         return self
 
-    def __radd__(self, args):
-        new = CompilerArgs(args, self.compiler)
+    def __radd__(self, args: typing.Iterable[str]):
+        new = CompilerArgs(self.compiler, args)
         new += self
         return new
 
-    def __eq__(self, other: object) -> bool:
-        '''
-        Only checks for equalety of self.compilers if the attribute is present.
-        Use the default list equalety for the compiler args.
-        '''
-        comp_eq = True
-        if hasattr(other, 'compiler'):
-            comp_eq = self.compiler == other.compiler
-        return comp_eq and super().__eq__(other)
-
-    def __mul__(self, args):  # lgtm[py/unexpected-raise-in-special-method]
-        raise TypeError("can't multiply compiler arguments")
-
-    def __imul__(self, args):  # lgtm[py/unexpected-raise-in-special-method]
-        raise TypeError("can't multiply compiler arguments")
+    def __eq__(self, other: typing.Any) -> typing.Union[bool, 'NotImplemented']:
+        # Only allow equality checks against other CompilerArgs and lists instances
+        if isinstance(other, CompilerArgs):
+            return self.compiler == other.compiler and self.__container == other.__container
+        elif isinstance(other, list):
+            return self.__container == other
+        return NotImplemented
 
-    def __rmul__(self, args):  # lgtm[py/unexpected-raise-in-special-method]
-        raise TypeError("can't multiply compiler arguments")
-
-    def append(self, arg):
+    def append(self, arg: str) -> None:
         self.__iadd__([arg])
 
-    def extend(self, args):
+    def extend(self, args: typing.Iterable[str]) -> None:
         self.__iadd__(args)
 
+    def __repr__(self) -> str:
+        return 'CompilerArgs({!r}, {!r})'.format(self.compiler, self.__container)
+
 class Compiler:
     # Libraries to ignore in find_library() since they are provided by the
     # compiler or the C library. Currently only used for MSVC.
diff --git a/run_unittests.py b/run_unittests.py
index 4e56c1d63..4260256e9 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -360,18 +360,14 @@ class InternalTests(unittest.TestCase):
     def test_compiler_args_class(self):
         cargsfunc = mesonbuild.compilers.CompilerArgs
         cc = mesonbuild.compilers.CCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock())
-        # Test that bad initialization fails
-        self.assertRaises(TypeError, cargsfunc, [])
-        self.assertRaises(TypeError, cargsfunc, [], [])
-        self.assertRaises(TypeError, cargsfunc, cc, [], [])
         # Test that empty initialization works
         a = cargsfunc(cc)
         self.assertEqual(a, [])
         # Test that list initialization works
-        a = cargsfunc(['-I.', '-I..'], cc)
+        a = cargsfunc(cc, ['-I.', '-I..'])
         self.assertEqual(a, ['-I.', '-I..'])
         # Test that there is no de-dup on initialization
-        self.assertEqual(cargsfunc(['-I.', '-I.'], cc), ['-I.', '-I.'])
+        self.assertEqual(cargsfunc(cc, ['-I.', '-I.']), ['-I.', '-I.'])
 
         ## Test that appending works
         a.append('-I..')