diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 45548e6875..bee09c7a5b 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -411,29 +411,6 @@ private extern(C++) final class Semantic3Visitor : Visitor } } -version (IN_LLVM) -{ - // Make sure semantic analysis has been run on argument types. This is - // e.g. needed for TypeTuple!(int, int) to be picked up as two int - // parameters by the Parameter functions. - for (size_t i = 0; i < f.parameterList.length; i++) - { - Parameter arg = f.parameterList[i]; - Type nw = arg.type.typeSemantic(Loc.initial, sc); - if (arg.type != nw) - { - arg.type = nw; - // Examine this index again. - // This is important if it turned into a tuple. - // In particular, the empty tuple should be handled or the - // next parameter will be skipped. - // LDC_FIXME: Maybe we only need to do this for tuples, - // and can add tuple.length after decrement? - i--; - } - } -} - /* Declare all the function parameters as variables * and install them in parameters[] */ diff --git a/gen/abi-aarch64.cpp b/gen/abi-aarch64.cpp index 39644b8464..03aed510c7 100644 --- a/gen/abi-aarch64.cpp +++ b/gen/abi-aarch64.cpp @@ -83,22 +83,25 @@ public: } // remove 0-sized args (static arrays with 0 elements) and, for Darwin, - // empty structs too + // empty POD structs too size_t i = 0; while (i < fty.args.size()) { - auto tb = fty.args[i]->type->toBasetype(); + auto arg = fty.args[i]; + if (!arg->byref) { + auto tb = arg->type->toBasetype(); - if (tb->size() == 0) { - fty.args.erase(fty.args.begin() + i); - continue; - } + if (tb->size() == 0) { + fty.args.erase(fty.args.begin() + i); + continue; + } - // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1 - if (isDarwin) { - if (auto ts = tb->isTypeStruct()) { - if (ts->sym->fields.empty()) { - fty.args.erase(fty.args.begin() + i); - continue; + // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1 + if (isDarwin) { + if (auto ts = tb->isTypeStruct()) { + if (ts->sym->fields.empty() && ts->sym->isPOD()) { + fty.args.erase(fty.args.begin() + i); + continue; + } } } } diff --git a/gen/functions.cpp b/gen/functions.cpp index a98a7aa7f5..7a54f8a168 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -815,22 +815,24 @@ void defineParameters(IrFuncTy &irFty, VarDeclarations ¶meters) { size_t llArgIdx = 0; for (VarDeclaration *vd : parameters) { + Type *paramType = vd->type; IrParameter *irparam = getIrParameter(vd); - // vd->type (parameter) and irparam->arg->type (argument) don't always - // match. - // E.g., for a lazy parameter of type T, vd->type is T (with lazy storage - // class) while irparam->arg->type is the delegate type. - Type *const paramType = (irparam ? irparam->arg->type : vd->type); - if (!irparam) { // This is a parameter that is not passed on the LLVM level. // Create the param here and set it to a "dummy" alloca that // we do not store to here. irparam = getIrParameter(vd, true); irparam->value = DtoAlloca(vd, vd->ident->toChars()); + } else if (!irparam->value) { + // Captured parameter not passed on the LLVM level. + assert(irparam->nestedIndex >= 0); + irparam->value = DtoAlloca(vd, vd->ident->toChars()); } else { - assert(irparam->value); + // vd->type (parameter) and irparam->arg->type (argument) don't always + // match. E.g., for a lazy parameter of type T, vd->type is T (with lazy + // storage class) while irparam->arg->type is the delegate type. + paramType = irparam->arg->type; if (irparam->arg->byref) { // The argument is an appropriate lvalue passed by reference. diff --git a/gen/nested.cpp b/gen/nested.cpp index 7b13d1bc8c..45bbd3152b 100644 --- a/gen/nested.cpp +++ b/gen/nested.cpp @@ -392,14 +392,17 @@ static void DtoCreateNestedContextType(FuncDeclaration *fd) { builder.alignCurrentOffset(alignment); } - IrLocal &irLocal = *getIrLocal(vd, true); + const bool isParam = vd->isParameter(); + + IrLocal &irLocal = + *(isParam ? getIrParameter(vd, true) : getIrLocal(vd, true)); irLocal.nestedIndex = builder.currentFieldIndex(); irLocal.nestedDepth = depth; LLType *t = nullptr; if (vd->isRef() || vd->isOut()) { t = DtoType(vd->type->pointerTo()); - } else if (vd->isParameter() && (vd->storage_class & STClazy)) { + } else if (isParam && (vd->storage_class & STClazy)) { // the type is a delegate (LL struct) Type *dt = TypeFunction::create(nullptr, vd->type, VARARGnone, LINKd); dt = createTypeDelegate(dt); diff --git a/tests/compilable/captured_nonpassed_params.d b/tests/compilable/captured_nonpassed_params.d new file mode 100644 index 0000000000..2e2a1d5dd7 --- /dev/null +++ b/tests/compilable/captured_nonpassed_params.d @@ -0,0 +1,35 @@ +// Some ABIs don't pass empty static arrays or empty PODs as arguments. +// This can get hairy, e.g., if such a parameter is captured. + +// RUN: %ldc -c -g %s + +struct EmptyPOD {} + +EmptyPOD emptyPOD(EmptyPOD e) +{ + EmptyPOD nested() { return e; } + nested(); + return e; +} + +int[0] emptySArray(int[0] a) +{ + int[0] nested() { return a; } + nested(); + return a; +} + +void tuple() +{ + auto test(T...)(T params) + { + auto nested() { return params[0]; } + nested(); + return params[0]; + } + + test(EmptyPOD()); + + int[0] a; + test(a); +}