From 5fdf5f3c68a0654ece7bbd369c5bfd80096fba18 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Fri, 11 Apr 2025 16:38:16 +0800 Subject: [PATCH] [NFC] outline class function semantic analysis out of `funcDeclarationSemantic` (#21182) This closes over several `goto`s whose target labels are now contained completely within the outlined code. --- compiler/src/dmd/funcsem.d | 1139 ++++++++++++++++++------------------ 1 file changed, 579 insertions(+), 560 deletions(-) diff --git a/compiler/src/dmd/funcsem.d b/compiler/src/dmd/funcsem.d index f21fe181d1..a7fc488fca 100644 --- a/compiler/src/dmd/funcsem.d +++ b/compiler/src/dmd/funcsem.d @@ -283,20 +283,6 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) if (!funcdecl.originalType) funcdecl.originalType = funcdecl.type.syntaxCopy(); - static TypeFunction getFunctionType(FuncDeclaration fd) - { - if (auto tf = fd.type.isTypeFunction()) - return tf; - - if (!fd.type.isTypeError()) - { - .error(fd.loc, "%s `%s` `%s` must be a function instead of `%s`", fd.kind, fd.toPrettyChars, fd.toChars(), fd.type.toChars()); - fd.type = Type.terror; - } - fd.errors = true; - return null; - } - if (sc.inCfile) { /* C11 allows a function to be declared with a typedef, D does not. @@ -573,554 +559,13 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) if (ClassDeclaration cd = parent.isClassDeclaration()) { - parent = cd = objc.getParent(funcdecl, cd); - - if (funcdecl.isCtorDeclaration()) + switch (classFuncSemantic(cd, funcdecl, parent, sc, f)) { - goto Ldone; + case 0: break; + case 1: goto Ldone; + case 2: return; + default: assert(0); } - - if (funcdecl.storage_class & STC.abstract_) - cd.isabstract = ThreeState.yes; - - // if static function, do not put in vtbl[] - if (!funcdecl.isVirtual()) - { - //printf("\tnot virtual\n"); - goto Ldone; - } - // Suppress further errors if the return type is an error - if (funcdecl.type.nextOf() == Type.terror) - goto Ldone; - - bool may_override = false; - for (size_t i = 0; i < cd.baseclasses.length; i++) - { - BaseClass* b = (*cd.baseclasses)[i]; - ClassDeclaration cbd = b.type.toBasetype().isClassHandle(); - if (!cbd) - continue; - for (size_t j = 0; j < cbd.vtbl.length; j++) - { - FuncDeclaration f2 = cbd.vtbl[j].isFuncDeclaration(); - if (!f2 || f2.ident != funcdecl.ident) - continue; - if (cbd.parent && cbd.parent.isTemplateInstance()) - { - if (!functionSemantic(f2)) - goto Ldone; - } - may_override = true; - } - } - if (may_override && funcdecl.type.nextOf() is null) - { - /* If same name function exists in base class but 'this' is auto return, - * cannot find index of base class's vtbl[] to override. - */ - .error(funcdecl.loc, "%s `%s` return type inference is not supported if may override base class function", funcdecl.kind, funcdecl.toPrettyChars); - } - - /* Find index of existing function in base class's vtbl[] to override - * (the index will be the same as in cd's current vtbl[]) - */ - int vi = cd.baseClass ? findVtblIndex(funcdecl, cd.baseClass.vtbl[]) : -1; - - bool doesoverride = false; - switch (vi) - { - case -1: - Lintro: - /* Didn't find one, so - * This is an 'introducing' function which gets a new - * slot in the vtbl[]. - */ - - // Verify this doesn't override previous final function - if (cd.baseClass) - { - Dsymbol s = cd.baseClass.search(funcdecl.loc, funcdecl.ident); - if (s) - { - if (auto f2 = s.isFuncDeclaration()) - { - f2 = f2.overloadExactMatch(funcdecl.type); - if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_) - .error(funcdecl.loc, "%s `%s` cannot override `final` function `%s`", funcdecl.kind, funcdecl.toPrettyChars, f2.toPrettyChars()); - } - } - } - - /* These quirky conditions mimic what happens when virtual - inheritance is implemented by producing a virtual base table - with offsets to each of the virtual bases. - */ - if (target.cpp.splitVBasetable && cd.classKind == ClassKind.cpp && - cd.baseClass && cd.baseClass.vtbl.length) - { - /* if overriding an interface function, then this is not - * introducing and don't put it in the class vtbl[] - */ - funcdecl.interfaceVirtual = overrideInterface(funcdecl); - if (funcdecl.interfaceVirtual) - { - //printf("\tinterface function %s\n", toChars()); - cd.vtblFinal.push(funcdecl); - goto Linterfaces; - } - } - - if (funcdecl.isFinalFunc()) - { - // Don't check here, as it may override an interface function - //if (isOverride()) - // error("is marked as override, but does not override any function"); - cd.vtblFinal.push(funcdecl); - } - else - { - //printf("\tintroducing function %s\n", funcdecl.toChars()); - funcdecl.isIntroducing = true; - if (cd.classKind == ClassKind.cpp && target.cpp.reverseOverloads) - { - /* Overloaded functions with same name are grouped and in reverse order. - * Search for first function of overload group, and insert - * funcdecl into vtbl[] immediately before it. - */ - funcdecl.vtblIndex = cast(int)cd.vtbl.length; - bool found; - foreach (const i, s; cd.vtbl) - { - if (found) - // the rest get shifted forward - ++s.isFuncDeclaration().vtblIndex; - else if (s.ident == funcdecl.ident && s.parent == parent) - { - // found first function of overload group - funcdecl.vtblIndex = cast(int)i; - found = true; - ++s.isFuncDeclaration().vtblIndex; - } - } - cd.vtbl.insert(funcdecl.vtblIndex, funcdecl); - - debug foreach (const i, s; cd.vtbl) - { - // a C++ dtor gets its vtblIndex later (and might even be added twice to the vtbl), - // e.g. when compiling druntime with a debug compiler, namely with core.stdcpp.exception. - if (auto fd = s.isFuncDeclaration()) - assert(fd.vtblIndex == i || - (cd.classKind == ClassKind.cpp && fd.isDtorDeclaration) || - funcdecl.parent.isInterfaceDeclaration); // interface functions can be in multiple vtbls - } - } - else - { - // Append to end of vtbl[] - vi = cast(int)cd.vtbl.length; - cd.vtbl.push(funcdecl); - funcdecl.vtblIndex = vi; - } - } - break; - - case -2: - // can't determine because of forward references - funcdecl.errors = true; - return; - - default: - { - if (vi >= cd.vtbl.length) - { - /* the derived class cd doesn't have its vtbl[] allocated yet. - * https://issues.dlang.org/show_bug.cgi?id=21008 - */ - .error(funcdecl.loc, "%s `%s` circular reference to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars()); - funcdecl.errors = true; - return; - } - FuncDeclaration fdv = cd.baseClass.vtbl[vi].isFuncDeclaration(); - FuncDeclaration fdc = cd.vtbl[vi].isFuncDeclaration(); - // This function is covariant with fdv - - if (fdc == funcdecl) - { - doesoverride = true; - break; - } - - auto vtf = getFunctionType(fdv); - if (vtf.trust > TRUST.system && f.trust == TRUST.system) - .error(funcdecl.loc, "%s `%s` cannot override `@safe` method `%s` with a `@system` attribute", funcdecl.kind, funcdecl.toPrettyChars, - fdv.toPrettyChars); - - if (fdc.toParent() == parent) - { - //printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n", - // vi, this, this.toChars(), this.type.toChars(), this.loc.toChars(), - // fdc, fdc .toChars(), fdc .type.toChars(), fdc .loc.toChars(), - // fdv, fdv .toChars(), fdv .type.toChars(), fdv .loc.toChars()); - - // fdc overrides fdv exactly, then this introduces new function. - if (fdc.type.mod == fdv.type.mod && funcdecl.type.mod != fdv.type.mod) - goto Lintro; - } - - if (fdv.isDeprecated && !funcdecl.isDeprecated) - deprecation(funcdecl.loc, "`%s` is overriding the deprecated method `%s`", - funcdecl.toPrettyChars, fdv.toPrettyChars); - - // This function overrides fdv - if (fdv.isFinalFunc()) - .error(funcdecl.loc, "%s `%s` cannot override `final` function `%s`", funcdecl.kind, funcdecl.toPrettyChars, fdv.toPrettyChars()); - - if (!funcdecl.isOverride()) - { - if (fdv.isFuture()) - { - deprecation(funcdecl.loc, "method `%s` implicitly overrides `@__future` base class method; rename the former", - funcdecl.toPrettyChars()); - deprecationSupplemental(fdv.loc, "base method `%s` defined here", - fdv.toPrettyChars()); - // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[] - goto Lintro; - } - else - { - // https://issues.dlang.org/show_bug.cgi?id=17349 - error(funcdecl.loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute", - fdv.toPrettyChars(), funcdecl.toPrettyChars()); - } - } - doesoverride = true; - if (fdc.toParent() == parent) - { - // If both are mixins, or both are not, then error. - // If either is not, the one that is not overrides the other. - bool thismixin = funcdecl.parent.isClassDeclaration() !is null; - bool fdcmixin = fdc.parent.isClassDeclaration() !is null; - if (thismixin == fdcmixin) - { - .error(funcdecl.loc, "%s `%s` multiple overrides of same function", funcdecl.kind, funcdecl.toPrettyChars); - } - /* - * https://issues.dlang.org/show_bug.cgi?id=711 - * - * If an overriding method is introduced through a mixin, - * we need to update the vtbl so that both methods are - * present. - */ - else if (thismixin) - { - /* if the mixin introduced the overriding method, then reintroduce it - * in the vtbl. The initial entry for the mixined method - * will be updated at the end of the enclosing `if` block - * to point to the current (non-mixined) function. - */ - auto vitmp = cast(int)cd.vtbl.length; - cd.vtbl.push(fdc); - fdc.vtblIndex = vitmp; - } - else if (fdcmixin) - { - /* if the current overriding function is coming from a - * mixined block, then push the current function in the - * vtbl, but keep the previous (non-mixined) function as - * the overriding one. - */ - auto vitmp = cast(int)cd.vtbl.length; - cd.vtbl.push(funcdecl); - funcdecl.vtblIndex = vitmp; - break; - } - else // fdc overrides fdv - { - // this doesn't override any function - break; - } - } - cd.vtbl[vi] = funcdecl; - funcdecl.vtblIndex = vi; - - /* Remember which functions this overrides - */ - funcdecl.foverrides.push(fdv); - - /* This works by whenever this function is called, - * it actually returns tintro, which gets dynamically - * cast to type. But we know that tintro is a base - * of type, so we could optimize it by not doing a - * dynamic cast, but just subtracting the isBaseOf() - * offset if the value is != null. - */ - - if (fdv.tintro) - funcdecl.tintro = fdv.tintro; - else if (!funcdecl.type.equals(fdv.type)) - { - auto tnext = funcdecl.type.nextOf(); - if (auto handle = tnext.isClassHandle()) - { - if (handle.semanticRun < PASS.semanticdone && !handle.isBaseInfoComplete()) - handle.dsymbolSemantic(null); - } - /* Only need to have a tintro if the vptr - * offsets differ - */ - int offset; - if (fdv.type.nextOf().isBaseOf(tnext, &offset)) - { - funcdecl.tintro = fdv.type; - } - } - break; - } - } - - /* Go through all the interface bases. - * If this function is covariant with any members of those interface - * functions, set the tintro. - */ - Linterfaces: - bool foundVtblMatch = false; - - for (ClassDeclaration bcd = cd; !foundVtblMatch && bcd; bcd = bcd.baseClass) - { - foreach (b; bcd.interfaces) - { - vi = findVtblIndex(funcdecl, b.sym.vtbl[]); - switch (vi) - { - case -1: - break; - - case -2: - // can't determine because of forward references - funcdecl.errors = true; - return; - - default: - { - auto fdv = cast(FuncDeclaration)b.sym.vtbl[vi]; - Type ti = null; - - foundVtblMatch = true; - - /* Remember which functions this overrides - */ - funcdecl.foverrides.push(fdv); - - if (fdv.tintro) - ti = fdv.tintro; - else if (!funcdecl.type.equals(fdv.type)) - { - /* Only need to have a tintro if the vptr - * offsets differ - */ - int offset; - if (fdv.type.nextOf().isBaseOf(funcdecl.type.nextOf(), &offset)) - { - ti = fdv.type; - } - } - if (ti) - { - if (funcdecl.tintro) - { - if (!funcdecl.tintro.nextOf().equals(ti.nextOf()) && !funcdecl.tintro.nextOf().isBaseOf(ti.nextOf(), null) && !ti.nextOf().isBaseOf(funcdecl.tintro.nextOf(), null)) - { - .error(funcdecl.loc, "%s `%s` incompatible covariant types `%s` and `%s`", funcdecl.kind, funcdecl.toPrettyChars, funcdecl.tintro.toChars(), ti.toChars()); - } - } - else - { - funcdecl.tintro = ti; - } - } - } - } - } - } - if (foundVtblMatch) - { - goto L2; - } - - if (!doesoverride && funcdecl.isOverride() && (funcdecl.type.nextOf() || !may_override)) - { - BaseClass* bc = null; - Dsymbol s = null; - for (size_t i = 0; i < cd.baseclasses.length; i++) - { - bc = (*cd.baseclasses)[i]; - s = bc.sym.search_correct(funcdecl.ident); - if (s) - break; - } - - if (s) - { - HdrGenState hgs; - OutBuffer buf; - - auto fd = s.isFuncDeclaration(); - functionToBufferFull(cast(TypeFunction)(funcdecl.type), buf, - new Identifier(funcdecl.toPrettyChars()), hgs, null); - const(char)* funcdeclToChars = buf.peekChars(); - - if (fd) - { - OutBuffer buf1; - - if (fd.ident == funcdecl.ident) - hgs.fullQual = true; - - // https://issues.dlang.org/show_bug.cgi?id=23745 - // If the potentially overridden function contains errors, - // inform the user to fix that one first - if (fd.errors) - { - error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?", - funcdecl.toChars(), fd.toPrettyChars()); - errorSupplemental(fd.loc, "Function `%s` contains errors in its declaration, therefore it cannot be correctly overridden", - fd.toPrettyChars()); - } - else if (fd.isFinalFunc()) - { - // When trying to override a final method, don't suggest it as a candidate(Issue #19613) - .error(funcdecl.loc, "%s `%s` does not override any function", funcdecl.kind, funcdecl.toPrettyChars); - - // Look for a non-final method with the same name to suggest as an alternative - auto cdparent = fd.parent ? fd.parent.isClassDeclaration() : null; - if (cdparent) - { - Dsymbol nonFinalAlt = null; - - auto overloadableSyms = cdparent.symtab.lookup(fd.ident); - if (overloadableSyms) - { - // Check each overload to find one that's not final - overloadApply(overloadableSyms, (Dsymbol s) - { - if (auto funcAlt = s.isFuncDeclaration()) - { - if (funcAlt != fd && !funcAlt.isFinalFunc()) - { - nonFinalAlt = funcAlt; - return 1; - } - } - return 0; - }); - - // Provide a helpful suggestion if we found a viable alternative - if (nonFinalAlt) - { - auto funcAlt = nonFinalAlt.isFuncDeclaration(); - OutBuffer buf2; - functionToBufferFull(cast(TypeFunction)(funcAlt.type), buf2, - new Identifier(funcAlt.toPrettyChars()), hgs, null); - errorSupplemental(funcdecl.loc, "Did you mean to override `%s`?", buf2.peekChars()); - } - } - } - } - else - { - functionToBufferFull(cast(TypeFunction)(fd.type), buf1, - new Identifier(fd.toPrettyChars()), hgs, null); - - error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?", - funcdeclToChars, buf1.peekChars()); - - // Supplemental error for parameter scope differences - auto tf1 = cast(TypeFunction)funcdecl.type; - auto tf2 = cast(TypeFunction)fd.type; - - if (tf1 && tf2) - { - auto params1 = tf1.parameterList; - auto params2 = tf2.parameterList; - - if (params1.length == params2.length) - { - bool hasScopeDifference = false; - - for (size_t i = 0; i < params1.length; i++) - { - auto p1 = params1[i]; - auto p2 = params2[i]; - - if ((p1.storageClass & STC.scope_) == (p2.storageClass & STC.scope_)) - continue; - - if (!(p2.storageClass & STC.scope_)) - continue; - - if (!hasScopeDifference) - { - // Intended signature - errorSupplemental(funcdecl.loc, "Did you intend to override:"); - errorSupplemental(funcdecl.loc, "`%s`", buf1.peekChars()); - hasScopeDifference = true; - } - errorSupplemental(funcdecl.loc, "Parameter %d is missing `scope`", - cast(int)(i + 1)); - } - } - } - } - } - else - { - error(funcdecl.loc, "function `%s` does not override any function, did you mean to override %s `%s`?", - funcdeclToChars, s.kind, s.toPrettyChars()); - errorSupplemental(funcdecl.loc, "Functions are the only declarations that may be overridden"); - } - } - else - .error(funcdecl.loc, "%s `%s` does not override any function", funcdecl.kind, funcdecl.toPrettyChars); - } - - L2: - objc.setSelector(funcdecl, sc); - objc.checkLinkage(funcdecl); - objc.addToClassMethodList(funcdecl, cd); - objc.setAsOptional(funcdecl, sc); - - /* Go through all the interface bases. - * Disallow overriding any final functions in the interface(s). - */ - foreach (b; cd.interfaces) - { - if (b.sym) - { - if (auto s = search_function(b.sym, funcdecl.ident)) - { - if (auto f2 = s.isFuncDeclaration()) - { - f2 = f2.overloadExactMatch(funcdecl.type); - if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_) - .error(funcdecl.loc, "%s `%s` cannot override `final` function `%s.%s`", funcdecl.kind, funcdecl.toPrettyChars, b.sym.toChars(), f2.toPrettyChars()); - } - } - } - } - - if (funcdecl.isOverride) - { - if (funcdecl.storage_class & STC.disable) - deprecation(funcdecl.loc, - "`%s` cannot be annotated with `@disable` because it is overriding a function in the base class", - funcdecl.toPrettyChars); - - if (funcdecl.isDeprecated && !(funcdecl.foverrides.length && funcdecl.foverrides[0].isDeprecated)) - deprecation(funcdecl.loc, - "`%s` cannot be marked as `deprecated` because it is overriding a function in the base class", - funcdecl.toPrettyChars); - } - } else if (funcdecl.isOverride() && !parent.isTemplateInstance()) .error(funcdecl.loc, "%s `%s` `override` only applies to class member functions", funcdecl.kind, funcdecl.toPrettyChars); @@ -1233,6 +678,580 @@ Ldone: } } +/** + Returns: + 0 if semantic analysis in `funcDeclarationSemantic` should continue as normal + 1 if it should skip over some analysis and `goto Ldone;` + 2 if `funcDeclarationSemantic` should return early because of forward refernce error or + the derived class cd doesn't have its vtbl[] allocated yet + */ +private int classFuncSemantic(ClassDeclaration cd, FuncDeclaration funcdecl, + ref Dsymbol parent, Scope* sc, TypeFunction f) +{ + parent = cd = objc.getParent(funcdecl, cd); + + if (funcdecl.isCtorDeclaration()) + { + return 1; + } + + if (funcdecl.storage_class & STC.abstract_) + cd.isabstract = ThreeState.yes; + + // if static function, do not put in vtbl[] + if (!funcdecl.isVirtual()) + { + //printf("\tnot virtual\n"); + return 1; + } + // Suppress further errors if the return type is an error + if (funcdecl.type.nextOf() == Type.terror) + return 1; + + bool may_override = false; + for (size_t i = 0; i < cd.baseclasses.length; i++) + { + BaseClass* b = (*cd.baseclasses)[i]; + ClassDeclaration cbd = b.type.toBasetype().isClassHandle(); + if (!cbd) + continue; + for (size_t j = 0; j < cbd.vtbl.length; j++) + { + FuncDeclaration f2 = cbd.vtbl[j].isFuncDeclaration(); + if (!f2 || f2.ident != funcdecl.ident) + continue; + if (cbd.parent && cbd.parent.isTemplateInstance()) + { + if (!functionSemantic(f2)) + return 1; + } + may_override = true; + } + } + if (may_override && funcdecl.type.nextOf() is null) + { + /* If same name function exists in base class but 'this' is auto return, + * cannot find index of base class's vtbl[] to override. + */ + .error(funcdecl.loc, "%s `%s` return type inference is not supported if may override base class function", funcdecl.kind, funcdecl.toPrettyChars); + } + + /* Find index of existing function in base class's vtbl[] to override + * (the index will be the same as in cd's current vtbl[]) + */ + int vi = cd.baseClass ? findVtblIndex(funcdecl, cd.baseClass.vtbl[]) : -1; + + bool doesoverride = false; + switch (vi) + { + case -1: + Lintro: + /* Didn't find one, so + * This is an 'introducing' function which gets a new + * slot in the vtbl[]. + */ + + // Verify this doesn't override previous final function + if (cd.baseClass) + { + Dsymbol s = cd.baseClass.search(funcdecl.loc, funcdecl.ident); + if (s) + { + if (auto f2 = s.isFuncDeclaration()) + { + f2 = f2.overloadExactMatch(funcdecl.type); + if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_) + .error(funcdecl.loc, "%s `%s` cannot override `final` function `%s`", funcdecl.kind, funcdecl.toPrettyChars, f2.toPrettyChars()); + } + } + } + + /* These quirky conditions mimic what happens when virtual + inheritance is implemented by producing a virtual base table + with offsets to each of the virtual bases. + */ + if (target.cpp.splitVBasetable && cd.classKind == ClassKind.cpp && + cd.baseClass && cd.baseClass.vtbl.length) + { + /* if overriding an interface function, then this is not + * introducing and don't put it in the class vtbl[] + */ + funcdecl.interfaceVirtual = overrideInterface(funcdecl); + if (funcdecl.interfaceVirtual) + { + //printf("\tinterface function %s\n", toChars()); + cd.vtblFinal.push(funcdecl); + goto Linterfaces; + } + } + + if (funcdecl.isFinalFunc()) + { + // Don't check here, as it may override an interface function + //if (isOverride()) + // error("is marked as override, but does not override any function"); + cd.vtblFinal.push(funcdecl); + } + else + { + //printf("\tintroducing function %s\n", funcdecl.toChars()); + funcdecl.isIntroducing = true; + if (cd.classKind == ClassKind.cpp && target.cpp.reverseOverloads) + { + /* Overloaded functions with same name are grouped and in reverse order. + * Search for first function of overload group, and insert + * funcdecl into vtbl[] immediately before it. + */ + funcdecl.vtblIndex = cast(int)cd.vtbl.length; + bool found; + foreach (const i, s; cd.vtbl) + { + if (found) + // the rest get shifted forward + ++s.isFuncDeclaration().vtblIndex; + else if (s.ident == funcdecl.ident && s.parent == parent) + { + // found first function of overload group + funcdecl.vtblIndex = cast(int)i; + found = true; + ++s.isFuncDeclaration().vtblIndex; + } + } + cd.vtbl.insert(funcdecl.vtblIndex, funcdecl); + + debug foreach (const i, s; cd.vtbl) + { + // a C++ dtor gets its vtblIndex later (and might even be added twice to the vtbl), + // e.g. when compiling druntime with a debug compiler, namely with core.stdcpp.exception. + if (auto fd = s.isFuncDeclaration()) + assert(fd.vtblIndex == i || + (cd.classKind == ClassKind.cpp && fd.isDtorDeclaration) || + funcdecl.parent.isInterfaceDeclaration); // interface functions can be in multiple vtbls + } + } + else + { + // Append to end of vtbl[] + vi = cast(int)cd.vtbl.length; + cd.vtbl.push(funcdecl); + funcdecl.vtblIndex = vi; + } + } + break; + + case -2: + // can't determine because of forward references + funcdecl.errors = true; + return 2; + + default: + { + if (vi >= cd.vtbl.length) + { + /* the derived class cd doesn't have its vtbl[] allocated yet. + * https://issues.dlang.org/show_bug.cgi?id=21008 + */ + .error(funcdecl.loc, "%s `%s` circular reference to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars()); + funcdecl.errors = true; + return 2; + } + FuncDeclaration fdv = cd.baseClass.vtbl[vi].isFuncDeclaration(); + FuncDeclaration fdc = cd.vtbl[vi].isFuncDeclaration(); + // This function is covariant with fdv + + if (fdc == funcdecl) + { + doesoverride = true; + break; + } + + auto vtf = getFunctionType(fdv); + if (vtf.trust > TRUST.system && f.trust == TRUST.system) + .error(funcdecl.loc, "%s `%s` cannot override `@safe` method `%s` with a `@system` attribute", funcdecl.kind, funcdecl.toPrettyChars, + fdv.toPrettyChars); + + if (fdc.toParent() == parent) + { + //printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n", + // vi, this, this.toChars(), this.type.toChars(), this.loc.toChars(), + // fdc, fdc .toChars(), fdc .type.toChars(), fdc .loc.toChars(), + // fdv, fdv .toChars(), fdv .type.toChars(), fdv .loc.toChars()); + + // fdc overrides fdv exactly, then this introduces new function. + if (fdc.type.mod == fdv.type.mod && funcdecl.type.mod != fdv.type.mod) + goto Lintro; + } + + if (fdv.isDeprecated && !funcdecl.isDeprecated) + deprecation(funcdecl.loc, "`%s` is overriding the deprecated method `%s`", + funcdecl.toPrettyChars, fdv.toPrettyChars); + + // This function overrides fdv + if (fdv.isFinalFunc()) + .error(funcdecl.loc, "%s `%s` cannot override `final` function `%s`", funcdecl.kind, funcdecl.toPrettyChars, fdv.toPrettyChars()); + + if (!funcdecl.isOverride()) + { + if (fdv.isFuture()) + { + deprecation(funcdecl.loc, "method `%s` implicitly overrides `@__future` base class method; rename the former", + funcdecl.toPrettyChars()); + deprecationSupplemental(fdv.loc, "base method `%s` defined here", + fdv.toPrettyChars()); + // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[] + goto Lintro; + } + else + { + // https://issues.dlang.org/show_bug.cgi?id=17349 + error(funcdecl.loc, "cannot implicitly override base class method `%s` with `%s`; add `override` attribute", + fdv.toPrettyChars(), funcdecl.toPrettyChars()); + } + } + doesoverride = true; + if (fdc.toParent() == parent) + { + // If both are mixins, or both are not, then error. + // If either is not, the one that is not overrides the other. + bool thismixin = funcdecl.parent.isClassDeclaration() !is null; + bool fdcmixin = fdc.parent.isClassDeclaration() !is null; + if (thismixin == fdcmixin) + { + .error(funcdecl.loc, "%s `%s` multiple overrides of same function", funcdecl.kind, funcdecl.toPrettyChars); + } + /* + * https://issues.dlang.org/show_bug.cgi?id=711 + * + * If an overriding method is introduced through a mixin, + * we need to update the vtbl so that both methods are + * present. + */ + else if (thismixin) + { + /* if the mixin introduced the overriding method, then reintroduce it + * in the vtbl. The initial entry for the mixined method + * will be updated at the end of the enclosing `if` block + * to point to the current (non-mixined) function. + */ + auto vitmp = cast(int)cd.vtbl.length; + cd.vtbl.push(fdc); + fdc.vtblIndex = vitmp; + } + else if (fdcmixin) + { + /* if the current overriding function is coming from a + * mixined block, then push the current function in the + * vtbl, but keep the previous (non-mixined) function as + * the overriding one. + */ + auto vitmp = cast(int)cd.vtbl.length; + cd.vtbl.push(funcdecl); + funcdecl.vtblIndex = vitmp; + break; + } + else // fdc overrides fdv + { + // this doesn't override any function + break; + } + } + cd.vtbl[vi] = funcdecl; + funcdecl.vtblIndex = vi; + + /* Remember which functions this overrides + */ + funcdecl.foverrides.push(fdv); + + /* This works by whenever this function is called, + * it actually returns tintro, which gets dynamically + * cast to type. But we know that tintro is a base + * of type, so we could optimize it by not doing a + * dynamic cast, but just subtracting the isBaseOf() + * offset if the value is != null. + */ + + if (fdv.tintro) + funcdecl.tintro = fdv.tintro; + else if (!funcdecl.type.equals(fdv.type)) + { + auto tnext = funcdecl.type.nextOf(); + if (auto handle = tnext.isClassHandle()) + { + if (handle.semanticRun < PASS.semanticdone && !handle.isBaseInfoComplete()) + handle.dsymbolSemantic(null); + } + /* Only need to have a tintro if the vptr + * offsets differ + */ + int offset; + if (fdv.type.nextOf().isBaseOf(tnext, &offset)) + { + funcdecl.tintro = fdv.type; + } + } + break; + } + } + + /* Go through all the interface bases. + * If this function is covariant with any members of those interface + * functions, set the tintro. + */ +Linterfaces: + bool foundVtblMatch = false; + + for (ClassDeclaration bcd = cd; !foundVtblMatch && bcd; bcd = bcd.baseClass) + { + foreach (b; bcd.interfaces) + { + vi = findVtblIndex(funcdecl, b.sym.vtbl[]); + switch (vi) + { + case -1: + break; + + case -2: + // can't determine because of forward references + funcdecl.errors = true; + return 2; + + default: + { + auto fdv = cast(FuncDeclaration)b.sym.vtbl[vi]; + Type ti = null; + + foundVtblMatch = true; + + /* Remember which functions this overrides + */ + funcdecl.foverrides.push(fdv); + + if (fdv.tintro) + ti = fdv.tintro; + else if (!funcdecl.type.equals(fdv.type)) + { + /* Only need to have a tintro if the vptr + * offsets differ + */ + int offset; + if (fdv.type.nextOf().isBaseOf(funcdecl.type.nextOf(), &offset)) + { + ti = fdv.type; + } + } + if (ti) + { + if (funcdecl.tintro) + { + if (!funcdecl.tintro.nextOf().equals(ti.nextOf()) && !funcdecl.tintro.nextOf().isBaseOf(ti.nextOf(), null) && !ti.nextOf().isBaseOf(funcdecl.tintro.nextOf(), null)) + { + .error(funcdecl.loc, "%s `%s` incompatible covariant types `%s` and `%s`", funcdecl.kind, funcdecl.toPrettyChars, funcdecl.tintro.toChars(), ti.toChars()); + } + } + else + { + funcdecl.tintro = ti; + } + } + } + } + } + } + if (foundVtblMatch) + { + goto L2; + } + + if (!doesoverride && funcdecl.isOverride() && (funcdecl.type.nextOf() || !may_override)) + { + BaseClass* bc = null; + Dsymbol s = null; + for (size_t i = 0; i < cd.baseclasses.length; i++) + { + bc = (*cd.baseclasses)[i]; + s = bc.sym.search_correct(funcdecl.ident); + if (s) + break; + } + + if (s) + { + HdrGenState hgs; + OutBuffer buf; + + auto fd = s.isFuncDeclaration(); + functionToBufferFull(cast(TypeFunction)(funcdecl.type), buf, + new Identifier(funcdecl.toPrettyChars()), hgs, null); + const(char)* funcdeclToChars = buf.peekChars(); + + if (fd) + { + OutBuffer buf1; + + if (fd.ident == funcdecl.ident) + hgs.fullQual = true; + + // https://issues.dlang.org/show_bug.cgi?id=23745 + // If the potentially overridden function contains errors, + // inform the user to fix that one first + if (fd.errors) + { + error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?", + funcdecl.toChars(), fd.toPrettyChars()); + errorSupplemental(fd.loc, "Function `%s` contains errors in its declaration, therefore it cannot be correctly overridden", + fd.toPrettyChars()); + } + else if (fd.isFinalFunc()) + { + // When trying to override a final method, don't suggest it as a candidate(Issue #19613) + .error(funcdecl.loc, "%s `%s` does not override any function", funcdecl.kind, funcdecl.toPrettyChars); + + // Look for a non-final method with the same name to suggest as an alternative + auto cdparent = fd.parent ? fd.parent.isClassDeclaration() : null; + if (cdparent) + { + Dsymbol nonFinalAlt = null; + + auto overloadableSyms = cdparent.symtab.lookup(fd.ident); + if (overloadableSyms) + { + // Check each overload to find one that's not final + overloadApply(overloadableSyms, (Dsymbol s) + { + if (auto funcAlt = s.isFuncDeclaration()) + { + if (funcAlt != fd && !funcAlt.isFinalFunc()) + { + nonFinalAlt = funcAlt; + return 1; + } + } + return 0; + }); + + // Provide a helpful suggestion if we found a viable alternative + if (nonFinalAlt) + { + auto funcAlt = nonFinalAlt.isFuncDeclaration(); + OutBuffer buf2; + functionToBufferFull(cast(TypeFunction)(funcAlt.type), buf2, + new Identifier(funcAlt.toPrettyChars()), hgs, null); + errorSupplemental(funcdecl.loc, "Did you mean to override `%s`?", buf2.peekChars()); + } + } + } + } + else + { + functionToBufferFull(cast(TypeFunction)(fd.type), buf1, + new Identifier(fd.toPrettyChars()), hgs, null); + + error(funcdecl.loc, "function `%s` does not override any function, did you mean to override `%s`?", + funcdeclToChars, buf1.peekChars()); + + // Supplemental error for parameter scope differences + auto tf1 = cast(TypeFunction)funcdecl.type; + auto tf2 = cast(TypeFunction)fd.type; + + if (tf1 && tf2) + { + auto params1 = tf1.parameterList; + auto params2 = tf2.parameterList; + + if (params1.length == params2.length) + { + bool hasScopeDifference = false; + + for (size_t i = 0; i < params1.length; i++) + { + auto p1 = params1[i]; + auto p2 = params2[i]; + + if ((p1.storageClass & STC.scope_) == (p2.storageClass & STC.scope_)) + continue; + + if (!(p2.storageClass & STC.scope_)) + continue; + + if (!hasScopeDifference) + { + // Intended signature + errorSupplemental(funcdecl.loc, "Did you intend to override:"); + errorSupplemental(funcdecl.loc, "`%s`", buf1.peekChars()); + hasScopeDifference = true; + } + errorSupplemental(funcdecl.loc, "Parameter %d is missing `scope`", + cast(int)(i + 1)); + } + } + } + } + } + else + { + error(funcdecl.loc, "function `%s` does not override any function, did you mean to override %s `%s`?", + funcdeclToChars, s.kind, s.toPrettyChars()); + errorSupplemental(funcdecl.loc, "Functions are the only declarations that may be overridden"); + } + } + else + .error(funcdecl.loc, "%s `%s` does not override any function", funcdecl.kind, funcdecl.toPrettyChars); + } + +L2: + objc.setSelector(funcdecl, sc); + objc.checkLinkage(funcdecl); + objc.addToClassMethodList(funcdecl, cd); + objc.setAsOptional(funcdecl, sc); + + /* Go through all the interface bases. + * Disallow overriding any final functions in the interface(s). + */ + foreach (b; cd.interfaces) + { + if (b.sym) + { + if (auto s = search_function(b.sym, funcdecl.ident)) + { + if (auto f2 = s.isFuncDeclaration()) + { + f2 = f2.overloadExactMatch(funcdecl.type); + if (f2 && f2.isFinalFunc() && f2.visible().kind != Visibility.Kind.private_) + .error(funcdecl.loc, "%s `%s` cannot override `final` function `%s.%s`", funcdecl.kind, funcdecl.toPrettyChars, b.sym.toChars(), f2.toPrettyChars()); + } + } + } + } + + if (funcdecl.isOverride) + { + if (funcdecl.storage_class & STC.disable) + deprecation(funcdecl.loc, + "`%s` cannot be annotated with `@disable` because it is overriding a function in the base class", + funcdecl.toPrettyChars); + + if (funcdecl.isDeprecated && !(funcdecl.foverrides.length && funcdecl.foverrides[0].isDeprecated)) + deprecation(funcdecl.loc, + "`%s` cannot be marked as `deprecated` because it is overriding a function in the base class", + funcdecl.toPrettyChars); + } + return 0; +} + +private TypeFunction getFunctionType(FuncDeclaration fd) +{ + if (auto tf = fd.type.isTypeFunction()) + return tf; + + if (!fd.type.isTypeError()) + { + .error(fd.loc, "%s `%s` `%s` must be a function instead of `%s`", fd.kind, fd.toPrettyChars, fd.toChars(), fd.type.toChars()); + fd.type = Type.terror; + } + fd.errors = true; + return null; +} + /***************************************** * Initialize for inferring the attributes of this function. */