5.12.5.2.2.2.1.3.7. 完成派生类的 RECORD_TYPE – 验证方法
接着,如果 t 是该派生类,就需要执行某些修正。回忆在 C++ 中,一旦把一个方法在基类中声明为虚函数,随后不管你是否把重载函数声明为虚函数,这些重载都被视为虚函数。而因为现在我们能同时看到派生类及基类,特定的语义检查是被期望的。
check_bases_and_members (continue)
4175 /* Check all the method declarations. */
4176 check_methods (t);
4177
4178 /* A nearly-empty class has to be vptr-containing; a nearly empty
4179 class contains just a vptr. */
4180 if (!TYPE_CONTAINS_VPTR_P (t))
4181 CLASSTYPE_NEARLY_EMPTY_P (t) = 0;
4182
4183 /* Do some bookkeeping that will guide the generation of implicitly
4184 declared member functions. */
4185 TYPE_HAS_COMPLEX_INIT_REF (t)
4186 |= (TYPE_HAS_INIT_REF (t)
4187 || TYPE_USES_VIRTUAL_BASECLASSES (t)
4188 || TYPE_POLYMORPHIC_P (t));
4189 TYPE_NEEDS_CONSTRUCTING (t)
4190 |= (TYPE_HAS_CONSTRUCTOR (t)
4191 || TYPE_USES_VIRTUAL_BASECLASSES (t)
4192 || TYPE_POLYMORPHIC_P (t));
4193 CLASSTYPE_NON_AGGREGATE (t) |= (TYPE_HAS_CONSTRUCTOR (t)
4194 || TYPE_POLYMORPHIC_P (t));
4195 CLASSTYPE_NON_POD_P (t)
4196 |= (CLASSTYPE_NON_AGGREGATE (t) || TYPE_HAS_DESTRUCTOR (t)
4197 || TYPE_HAS_ASSIGN_REF (t));
4198 TYPE_HAS_REAL_ASSIGN_REF (t) |= TYPE_HAS_ASSIGN_REF (t);
4199 TYPE_HAS_COMPLEX_ASSIGN_REF (t)
4200 |= TYPE_HAS_ASSIGN_REF (t) || TYPE_CONTAINS_VPTR_P (t);
4201
4202 /* Synthesize any needed methods. Note that methods will be synthesized
4203 for anonymous unions; grok_x_components undoes that. */
4204 add_implicitly_declared_members (t, cant_have_default_ctor,
4205 cant_have_const_ctor,
4206 no_const_asn_ref);
下面的 DECL_VINDEX ,如果是非 0 值,指向在一个基类中的一个 FUNCTION_DECL ,它将被该 FUNCTION_DECL (即 x )作为一个虚函数来替换。那么对于该类,应该设置 TYPE_POLYMORPHIC_P 来表示它包含了虚函数。进一步的如果该方法是纯虚的( DECL_PURE_VIRTUAL_P 返回非 0 值),因为这种类型的类不能被具现,它应该被串接入 CLASSTYPE_PURE_VIRTUALS 域的链表中。
3770 static void
3771 check_methods (tree t) in class.c
3772 {
3773 tree x;
3774
3775 for (x = TYPE_METHODS (t); x; x = TREE_CHAIN (x))
3776 {
3777 /* If this was an evil function, don't keep it in class. */
3778 if (DECL_ASSEMBLER_NAME_SET_P (x)
3779 && IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (x)))
3780 continue ;
3781
3782 check_for_override (x, t);
3783 if (DECL_PURE_VIRTUAL_P (x) && ! DECL_VINDEX (x))
3784 cp_error_at ("initializer specified for non-virtual method `%D'", x);
3785
3786 /* The name of the field is the original field name
3787 Save this in auxiliary field for later overloading. */
3788 if (DECL_VINDEX (x))
3789 {
3790 TYPE_POLYMORPHIC_P (t) = 1;
3791 if (DECL_PURE_VIRTUAL_P (x))
3792 CLASSTYPE_PURE_VIRTUALS (t)
3793 = tree_cons (NULL_TREE, x, CLASSTYPE_PURE_VIRTUALS (t));
3794 }
3795 }
3796 }
对于普通的方法,在其解析期间的语义检查是足够好的。不过这里有一个例外,在 C++ 中,如果一个方法在基类中被声明为虚函数,在派生类中其重载亦被视为虚函数,而不管其是否被声明为虚函数。这里通过 check_for_override 进行这个修正。
2423 static void
2424 check_for_override (tree decl, tree ctype) in class.c
2425 {
2426 if (TREE_CODE (decl) == TEMPLATE_DECL)
2427 /* In [temp.mem] we have:
2428
2429 A specialization of a member function template does not
2430 override a virtual function from a base class. */
2431 return ;
2432 if ((DECL_DESTRUCTOR_P (decl)
2433 || IDENTIFIER_VIRTUAL_P (DECL_NAME (decl))
2434 || DECL_CONV_FN_P (decl))
2435 && look_for_overrides (ctype, decl)
2436 && !DECL_STATIC_FUNCTION_P (decl))
2437 /* Set DECL_VINDEX to a value that is neither an INTEGER_CST nor
2438 the error_mark_node so that we know it is an overriding
2439 function. */
2440 DECL_VINDEX (decl) = decl;
2441
2442 if (DECL_VIRTUAL_P (decl))
2443 {
2444 if (!DECL_VINDEX (decl))
2445 DECL_VINDEX (decl) = error_mark_node;
2446 IDENTIFIER_VIRTUAL_P (DECL_NAME (decl)) = 1;
2447 }
2448 }
DECL_DESTRUCTOR_P 是非 0 值,如果该节点是一个析构函数; DECL_CONV_FN_P 是非 0 值,如果该节点是一个用户定义的转换操作符;而 IDENTIFIER_VIRTUAL_P 是非 0 值,如果该标识符在某些地方被用作一个虚函数的名字。我们需要检查这 3 种情形是因为不像普通虚函数的名字在派生的过程中是不变的,并且一旦某个声明被说明为虚函数就设置 IDENTIFIER_VIRTUAL_P ;析构函数与用户定义转换操作符在派生类中必须使用不同的名字。
1849 int
1850 look_for_overrides (tree type, tree fndecl) in search.c
1851 {
1852 tree binfo = TYPE_BINFO (type);
1853 tree basebinfos = BINFO_BASETYPES (binfo);
1854 int nbasebinfos = basebinfos ? TREE_VEC_LENGTH (basebinfos) : 0;
1855 int ix;
1856 int found = 0;
1857
1858 for (ix = 0; ix != nbasebinfos; ix++)
1859 {
1860 tree basetype = BINFO_TYPE (TREE_VEC_ELT (basebinfos, ix));
1861
1862 if (TYPE_POLYMORPHIC_P (basetype))
1863 found += look_for_overrides_r (basetype, fndecl);
1864 }
1865 return found;
1866 }
TYPE_POLYMORPHIC_P 不为 0 当且仅当一个类声明或继承了一个虚函数。它告诉我们查找虚函数声明时应该查询哪些基类。
1909 static int
1910 look_for_overrides_r (tree type, tree fndecl) in search.c
1911 {
1912 tree fn = look_for_overrides_here (type, fndecl);
1913 if (fn)
1914 {
1915 if (DECL_STATIC_FUNCTION_P (fndecl))
1916 {
1917 /* A static member function cannot match an inherited
1918 virtual member function. */
1919 cp_error_at ("`%#D' cannot be declared", fndecl);
1920 cp_error_at (" since `%#D' declared in base class", fn);
1921 }
1922 else
1923 {
1924 /* It's definitely virtual, even if not explicitly set. */
1925 DECL_VIRTUAL_P (fndecl) = 1;
1926 check_final_overrider (fndecl, fn);
1927 }
1928 return 1;
1929 }
1930
1931 /* We failed to find one declared in this class. Look in its bases. */
1932 return look_for_overrides (type, fndecl);
1933 }
看到如果 look_for_overrides_here 在基类中找出所指定的虚函数声明,在 1925 行这个目标声明被标记为虚( virtual )。
1871 tree
1872 look_for_overrides_here (tree type, tree fndecl) in search.c
1873 {
1874 int ix;
1875
1876 if (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fndecl))
1877 ix = CLASSTYPE_DESTRUCTOR_SLOT;
1878 else
1879 ix = lookup_fnfields_1 (type, DECL_NAME (fndecl));
1880 if (ix >= 0)
1881 {
1882 tree fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), ix);
1883
1884 for (; fns; fns = OVL_NEXT (fns))
1885 {
1886 tree fn = OVL_CURRENT (fns);
1887
1888 if (!DECL_VIRTUAL_P (fn))
1889 /* Not a virtual. */;
1890 else if (DECL_CONTEXT (fn) != type)
1891 /* Introduced with a using declaration. */;
1892 else if (DECL_STATIC_FUNCTION_P (fndecl))
1893 {
1894 tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn));
1895 tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
1896 if (compparms (TREE_CHAIN (btypes), dtypes))
1897 return fn;
1898 }
1899 else if (same_signature_p (fndecl, fn))
1900 return fn;
1901 }
1902 }
1903 return NULL_TREE;
1904 }
用户可能在基类中定义一个虚函数,然后在派生类中把它重载为静态方法。对于这种情形, look_for_overrides_here 必须能找出这个基类的虚函数。不过,这样的定义在 C++ 中是不被允许的,在 look_for_overrides_here 的调用之后, look_for_overrides_r 立即给出出错消息。
即便对于非静态方法,要成为基类虚函数的一个有效的重载也需要遵守某些规则。首先,两者的返回类型必须兼容。它们要么是相同的,要么基类方法返回的类型也是派生类方法返回类型的基类,并且两者具有相同的 c-v 限定词及引用层次,两者应该抛出兼容的异常。
1743 int
1744 check_final_overrider (tree overrider, tree basefn) in search.c
1745 {
1746 tree over_type = TREE_TYPE (overrider);
1747 tree base_type = TREE_TYPE (basefn);
1748 tree over_return = TREE_TYPE (over_type);
1749 tree base_return = TREE_TYPE (base_type);
1750 tree over_throw = TYPE_RAISES_EXCEPTIONS (over_type);
1751 tree base_throw = TYPE_RAISES_EXCEPTIONS (base_type);
1752 int fail = 0;
1753
1754 if (same_type_p (base_return, over_return))
1755 /* OK */;
1756 else if ((CLASS_TYPE_P (over_return) && CLASS_TYPE_P (base_return))
1757 || (TREE_CODE (base_return) == TREE_CODE (over_return)
1758 && POINTER_TYPE_P (base_return)))
1759 {
1760 /* Potentially covariant. */
1761 unsigned base_quals, over_quals;
1762
1763 fail = !POINTER_TYPE_P (base_return);
1764 if (!fail)
1765 {
1766 fail = cp_type_quals (base_return) != cp_type_quals (over_return);
1767
1768 base_return = TREE_TYPE (base_return);
1769 over_return = TREE_TYPE (over_return);
1770 }
1771 base_quals = cp_type_quals (base_return);
1772 over_quals = cp_type_quals (over_return);
1773
1774 if ((base_quals & over_quals) != over_quals)
1775 fail = 1;
1776
1777 if (CLASS_TYPE_P (base_return) && CLASS_TYPE_P (over_return))
1778 {
1779 tree binfo = lookup_base (over_return, base_return,
1780 ba_check | ba_quiet, NULL);
1781
1782 if (!binfo)
1783 fail = 1;
1784 }
1785 else if (!pedantic
1786 && can_convert (TREE_TYPE (base_type), TREE_TYPE (over_type)))
1787 /* GNU extension, allow trivial pointer conversions such as
1788 converting to void *, or qualification conversion. */
1789 {
1790 /* can_convert will permit user defined conversion from a
1791 (reference to) class type. We must reject them. */
1792 over_return = non_reference (TREE_TYPE (over_type));
1793 if (CLASS_TYPE_P (over_return))
1794 fail = 2;
1795 }
1796 else
1797 fail = 2;
1798 }
1799 else
1800 fail = 2;
1801 if (!fail)
1802 /* OK */;
1803 else if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider)))
1804 return 0;
1805 else
1806 {
1807 if (fail == 1)
1808 {
1809 cp_error_at ("invalid covariant return type for `%#D'", overrider);
1810 cp_error_at (" overriding `%#D'", basefn);
1811 }
1812 else
1813 {
1814 cp_error_at ("conflicting return type specified for `%#D'",
1815 overrider);
1816 cp_error_at (" overriding `%#D'", basefn);
1817 }
1818 SET_IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider),
1819 DECL_CONTEXT (overrider));
1820 return 0;
1821 }
1822
1823 /* Check throw specifier is at least as strict. */
1824 if (!comp_except_specs (base_throw, over_throw, 0))
1825 {
1826 if (!IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider)))
1827 {
1828 cp_error_at ("looser throw specifier for `%#F'", overrider);
1829 cp_error_at (" overriding `%#F'", basefn);
1830 SET_IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider),
1831 DECL_CONTEXT (overrider));
1832 }
1833 return 0;
1834 }
1835
1836 return 1;
1837 }