[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.
This commit is contained in:
Nicholas Wilson 2025-04-11 16:38:16 +08:00 committed by GitHub
parent 493cd034a9
commit 5fdf5f3c68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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.
@ -572,12 +558,141 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
}
if (ClassDeclaration cd = parent.isClassDeclaration())
{
switch (classFuncSemantic(cd, funcdecl, parent, sc, f))
{
case 0: break;
case 1: goto Ldone;
case 2: return;
default: assert(0);
}
}
else if (funcdecl.isOverride() && !parent.isTemplateInstance())
.error(funcdecl.loc, "%s `%s` `override` only applies to class member functions", funcdecl.kind, funcdecl.toPrettyChars);
if (auto ti = parent.isTemplateInstance)
{
objc.setSelector(funcdecl, sc);
objc.setAsOptional(funcdecl, sc);
}
objc.validateSelector(funcdecl);
objc.validateOptional(funcdecl);
// Reflect this.type to f because it could be changed by findVtblIndex
f = funcdecl.type.toTypeFunction();
Ldone:
if (!funcdecl.fbody && !funcdecl.allowsContractWithoutBody())
.error(funcdecl.loc, "%s `%s` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract", funcdecl.kind, funcdecl.toPrettyChars);
/* Do not allow template instances to add virtual functions
* to a class.
*/
if (funcdecl.isVirtual())
{
if (auto ti = parent.isTemplateInstance())
{
// Take care of nested templates
while (1)
{
TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
if (!ti2)
break;
ti = ti2;
}
// If it's a member template
if (ClassDeclaration cd = ti.tempdecl.isClassMember())
{
.error(funcdecl.loc, "%s `%s` cannot use template to add virtual function to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars());
}
}
}
funcdecl.checkMain(); // Check main() parameters and return type
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
if (funcdecl.canInferAttributes(sc))
funcdecl.initInferAttributes();
funcdecl.semanticRun = PASS.semanticdone;
/* Save scope for possible later use (if we need the
* function internals)
*/
funcdecl._scope = sc.copy();
funcdecl._scope.setNoFree();
__gshared bool printedMain = false; // semantic might run more than once
if (global.params.v.verbose && !printedMain)
{
const(char)* type = funcdecl.isMain() ? "main" : funcdecl.isWinMain() ? "winmain" : funcdecl.isDllMain() ? "dllmain" : cast(const(char)*)null;
Module mod = sc._module;
if (type && mod)
{
printedMain = true;
auto name = mod.srcfile.toChars();
auto path = FileName.searchPath(global.importPaths, name, true);
message("entry %-10s\t%s", type, path ? path : name);
}
}
if (funcdecl.fbody && sc._module.isRoot() &&
(funcdecl.isMain() || funcdecl.isWinMain() || funcdecl.isDllMain() || funcdecl.isCMain()))
global.hasMainFunction = true;
if (funcdecl.fbody && funcdecl.isMain() && sc._module.isRoot())
{
// check if `_d_cmain` is defined
bool cmainTemplateExists()
{
Dsymbol pscopesym;
auto rootSymbol = sc.search(funcdecl.loc, Id.empty, pscopesym);
if (auto moduleSymbol = rootSymbol.search(funcdecl.loc, Id.object))
if (moduleSymbol.search(funcdecl.loc, Id.CMain))
return true;
return false;
}
// Only mixin `_d_cmain` if it is defined
if (cmainTemplateExists())
{
// add `mixin _d_cmain!();` to the declaring module
auto tqual = new TypeIdentifier(funcdecl.loc, Id.CMain);
auto tm = new TemplateMixin(funcdecl.loc, null, tqual, null);
sc._module.members.push(tm);
}
}
assert(funcdecl.type.ty != Terror || funcdecl.errors);
// semantic for parameters' UDAs
foreach (i, param; f.parameterList)
{
if (param && param.userAttribDecl)
param.userAttribDecl.dsymbolSemantic(sc);
}
}
/**
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())
{
goto Ldone;
return 1;
}
if (funcdecl.storage_class & STC.abstract_)
@ -587,11 +702,11 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
if (!funcdecl.isVirtual())
{
//printf("\tnot virtual\n");
goto Ldone;
return 1;
}
// Suppress further errors if the return type is an error
if (funcdecl.type.nextOf() == Type.terror)
goto Ldone;
return 1;
bool may_override = false;
for (size_t i = 0; i < cd.baseclasses.length; i++)
@ -608,7 +723,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
if (cbd.parent && cbd.parent.isTemplateInstance())
{
if (!functionSemantic(f2))
goto Ldone;
return 1;
}
may_override = true;
}
@ -727,7 +842,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
case -2:
// can't determine because of forward references
funcdecl.errors = true;
return;
return 2;
default:
{
@ -738,7 +853,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
*/
.error(funcdecl.loc, "%s `%s` circular reference to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars());
funcdecl.errors = true;
return;
return 2;
}
FuncDeclaration fdv = cd.baseClass.vtbl[vi].isFuncDeclaration();
FuncDeclaration fdc = cd.vtbl[vi].isFuncDeclaration();
@ -898,7 +1013,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
case -2:
// can't determine because of forward references
funcdecl.errors = true;
return;
return 2;
default:
{
@ -1120,117 +1235,21 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
"`%s` cannot be marked as `deprecated` because it is overriding a function in the base class",
funcdecl.toPrettyChars);
}
return 0;
}
else if (funcdecl.isOverride() && !parent.isTemplateInstance())
.error(funcdecl.loc, "%s `%s` `override` only applies to class member functions", funcdecl.kind, funcdecl.toPrettyChars);
if (auto ti = parent.isTemplateInstance)
private TypeFunction getFunctionType(FuncDeclaration fd)
{
objc.setSelector(funcdecl, sc);
objc.setAsOptional(funcdecl, sc);
}
if (auto tf = fd.type.isTypeFunction())
return tf;
objc.validateSelector(funcdecl);
objc.validateOptional(funcdecl);
// Reflect this.type to f because it could be changed by findVtblIndex
f = funcdecl.type.toTypeFunction();
Ldone:
if (!funcdecl.fbody && !funcdecl.allowsContractWithoutBody())
.error(funcdecl.loc, "%s `%s` `in` and `out` contracts can only appear without a body when they are virtual interface functions or abstract", funcdecl.kind, funcdecl.toPrettyChars);
/* Do not allow template instances to add virtual functions
* to a class.
*/
if (funcdecl.isVirtual())
if (!fd.type.isTypeError())
{
if (auto ti = parent.isTemplateInstance())
{
// Take care of nested templates
while (1)
{
TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
if (!ti2)
break;
ti = ti2;
}
// If it's a member template
if (ClassDeclaration cd = ti.tempdecl.isClassMember())
{
.error(funcdecl.loc, "%s `%s` cannot use template to add virtual function to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars());
}
}
}
funcdecl.checkMain(); // Check main() parameters and return type
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
if (funcdecl.canInferAttributes(sc))
funcdecl.initInferAttributes();
funcdecl.semanticRun = PASS.semanticdone;
/* Save scope for possible later use (if we need the
* function internals)
*/
funcdecl._scope = sc.copy();
funcdecl._scope.setNoFree();
__gshared bool printedMain = false; // semantic might run more than once
if (global.params.v.verbose && !printedMain)
{
const(char)* type = funcdecl.isMain() ? "main" : funcdecl.isWinMain() ? "winmain" : funcdecl.isDllMain() ? "dllmain" : cast(const(char)*)null;
Module mod = sc._module;
if (type && mod)
{
printedMain = true;
auto name = mod.srcfile.toChars();
auto path = FileName.searchPath(global.importPaths, name, true);
message("entry %-10s\t%s", type, path ? path : name);
}
}
if (funcdecl.fbody && sc._module.isRoot() &&
(funcdecl.isMain() || funcdecl.isWinMain() || funcdecl.isDllMain() || funcdecl.isCMain()))
global.hasMainFunction = true;
if (funcdecl.fbody && funcdecl.isMain() && sc._module.isRoot())
{
// check if `_d_cmain` is defined
bool cmainTemplateExists()
{
Dsymbol pscopesym;
auto rootSymbol = sc.search(funcdecl.loc, Id.empty, pscopesym);
if (auto moduleSymbol = rootSymbol.search(funcdecl.loc, Id.object))
if (moduleSymbol.search(funcdecl.loc, Id.CMain))
return true;
return false;
}
// Only mixin `_d_cmain` if it is defined
if (cmainTemplateExists())
{
// add `mixin _d_cmain!();` to the declaring module
auto tqual = new TypeIdentifier(funcdecl.loc, Id.CMain);
auto tm = new TemplateMixin(funcdecl.loc, null, tqual, null);
sc._module.members.push(tm);
}
}
assert(funcdecl.type.ty != Terror || funcdecl.errors);
// semantic for parameters' UDAs
foreach (i, param; f.parameterList)
{
if (param && param.userAttribDecl)
param.userAttribDecl.dsymbolSemantic(sc);
.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;
}
/*****************************************