Skip to content

Commit 8f4adb3

Browse files
authored
Merge pull request #71207 from DougGregor/typed-throws-in-main-type
Allow the main function for `@main` types to use typed throws
2 parents 3ed0932 + fcec8b5 commit 8f4adb3

File tree

11 files changed

+223
-116
lines changed

11 files changed

+223
-116
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ FUNC_DECL(ConvertToAnyHashable, "_convertToAnyHashable")
6666

6767
FUNC_DECL(DiagnoseUnexpectedError, "_unexpectedError")
6868
FUNC_DECL(DiagnoseUnexpectedErrorTyped, "_unexpectedErrorTyped")
69+
FUNC_DECL(ErrorInMainTyped, "_errorInMainTyped")
6970
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
7071
FUNC_DECL(DiagnoseUnexpectedEnumCase, "_diagnoseUnexpectedEnumCase")
7172
FUNC_DECL(DiagnoseUnexpectedEnumCaseValue, "_diagnoseUnexpectedEnumCaseValue")

lib/AST/ASTContext.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,21 @@ FuncDecl *ASTContext::getAsyncIteratorNext() const {
965965
return nullptr;
966966
}
967967

968+
namespace {
969+
970+
template<typename DeclClass>
971+
DeclClass *synthesizeBuiltinDecl(const ASTContext &ctx, StringRef name) {
972+
if (name == "Never") {
973+
auto never = new (ctx) EnumDecl(SourceLoc(), ctx.getIdentifier(name),
974+
SourceLoc(), { }, nullptr,
975+
ctx.MainModule);
976+
return (DeclClass *)never;
977+
}
978+
return nullptr;
979+
}
980+
981+
}
982+
968983
#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
969984
DECL_CLASS *ASTContext::get##NAME##Decl() const { \
970985
if (getImpl().NAME##Decl) \
@@ -980,7 +995,8 @@ DECL_CLASS *ASTContext::get##NAME##Decl() const { \
980995
} \
981996
} \
982997
} \
983-
return nullptr; \
998+
getImpl().NAME##Decl = synthesizeBuiltinDecl<DECL_CLASS>(*this, #NAME); \
999+
return getImpl().NAME##Decl; \
9841000
} \
9851001
\
9861002
Type ASTContext::get##NAME##Type() const { \

lib/AST/Type.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5700,7 +5700,7 @@ llvm::Optional<Type> AnyFunctionType::getEffectiveThrownErrorType() const {
57005700
return getASTContext().getErrorExistentialType();
57015701

57025702
// If the thrown interface type is "Never", this function does not throw.
5703-
if (thrownError->isEqual(getASTContext().getNeverType()))
5703+
if (thrownError->isNever())
57045704
return llvm::None;
57055705

57065706
// Otherwise, return the typed error.

lib/SILGen/SILGenExpr.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,6 @@ void SILGenFunction::ForceTryEmission::finish() {
12081208
}, LookUpConformanceInModule(SGF.getModule().getSwiftModule()));
12091209

12101210
// Generic errors are passed indirectly.
1211-
#if true
12121211
if (!error.getType().isAddress()) {
12131212
auto *tmp = SGF.B.createAllocStack(Loc,
12141213
error.getType().getObjectType(),
@@ -1218,11 +1217,6 @@ void SILGenFunction::ForceTryEmission::finish() {
12181217

12191218
tmpBuffer = tmp;
12201219
}
1221-
#else
1222-
error = SGF.emitSubstToOrigValue(Loc, error,
1223-
AbstractionPattern::getOpaque(),
1224-
error.getType().getASTType());
1225-
#endif
12261220
}
12271221

12281222
SGF.emitApplyOfLibraryIntrinsic(

lib/SILGen/SILGenFunction.cpp

Lines changed: 1 addition & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,89 +1319,7 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
13191319

13201320
case ArtificialMainKind::TypeMain: {
13211321
// Emit a call to the main static function.
1322-
// return Module.$main();
1323-
auto *mainFunc = cast<FuncDecl>(mainDecl);
1324-
auto moduleLoc = SILLocation(mainDecl);
1325-
auto *entryBlock = B.getInsertionBB();
1326-
1327-
SILDeclRef mainFunctionDeclRef(mainFunc, SILDeclRef::Kind::Func);
1328-
SILFunction *mainFunction =
1329-
SGM.getFunction(mainFunctionDeclRef, NotForDefinition);
1330-
1331-
ExtensionDecl *mainExtension =
1332-
dyn_cast<ExtensionDecl>(mainFunc->getDeclContext());
1333-
1334-
NominalTypeDecl *mainType;
1335-
if (mainExtension) {
1336-
mainType = mainExtension->getExtendedNominal();
1337-
} else {
1338-
mainType = cast<NominalTypeDecl>(mainFunc->getDeclContext());
1339-
}
1340-
auto metatype = B.createMetatype(mainType, getLoweredType(mainType->getInterfaceType()));
1341-
1342-
auto mainFunctionRef = B.createFunctionRef(moduleLoc, mainFunction);
1343-
1344-
auto builtinInt32Type = SILType::getBuiltinIntegerType(32, getASTContext());
1345-
1346-
auto *exitBlock = createBasicBlock();
1347-
SILValue exitCode =
1348-
exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None);
1349-
B.setInsertionPoint(exitBlock);
1350-
1351-
if (!mainFunc->hasAsync()) {
1352-
auto returnType = F.getConventions().getSingleSILResultType(
1353-
B.getTypeExpansionContext());
1354-
if (exitCode->getType() != returnType)
1355-
exitCode = B.createStruct(moduleLoc, returnType, exitCode);
1356-
B.createReturn(moduleLoc, exitCode);
1357-
} else {
1358-
FuncDecl *exitFuncDecl = SGM.getExit();
1359-
assert(exitFuncDecl && "Failed to find exit function declaration");
1360-
SILFunction *exitSILFunc = SGM.getFunction(
1361-
SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/ true),
1362-
NotForDefinition);
1363-
1364-
SILFunctionType &funcType =
1365-
*exitSILFunc->getLoweredType().getAs<SILFunctionType>();
1366-
SILType retType = SILType::getPrimitiveObjectType(
1367-
funcType.getParameters().front().getInterfaceType());
1368-
exitCode = B.createStruct(moduleLoc, retType, exitCode);
1369-
SILValue exitCall = B.createFunctionRef(moduleLoc, exitSILFunc);
1370-
B.createApply(moduleLoc, exitCall, {}, {exitCode});
1371-
B.createUnreachable(moduleLoc);
1372-
}
1373-
1374-
if (mainFunc->hasThrows()) {
1375-
auto *successBlock = createBasicBlock();
1376-
B.setInsertionPoint(successBlock);
1377-
successBlock->createPhiArgument(SGM.Types.getEmptyTupleType(),
1378-
OwnershipKind::None);
1379-
SILValue zeroReturnValue =
1380-
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 0);
1381-
B.createBranch(moduleLoc, exitBlock, {zeroReturnValue});
1382-
1383-
auto *failureBlock = createBasicBlock();
1384-
B.setInsertionPoint(failureBlock);
1385-
SILValue error = failureBlock->createPhiArgument(
1386-
SILType::getExceptionType(getASTContext()), OwnershipKind::Owned);
1387-
// Log the error.
1388-
B.createBuiltin(moduleLoc, getASTContext().getIdentifier("errorInMain"),
1389-
SGM.Types.getEmptyTupleType(), {}, {error});
1390-
B.createEndLifetime(moduleLoc, error);
1391-
SILValue oneReturnValue =
1392-
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 1);
1393-
B.createBranch(moduleLoc, exitBlock, {oneReturnValue});
1394-
1395-
B.setInsertionPoint(entryBlock);
1396-
B.createTryApply(moduleLoc, mainFunctionRef, SubstitutionMap(),
1397-
{metatype}, successBlock, failureBlock);
1398-
} else {
1399-
B.setInsertionPoint(entryBlock);
1400-
B.createApply(moduleLoc, mainFunctionRef, SubstitutionMap(), {metatype});
1401-
SILValue returnValue =
1402-
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 0);
1403-
B.createBranch(moduleLoc, exitBlock, {returnValue});
1404-
}
1322+
emitCallToMain(cast<FuncDecl>(mainDecl));
14051323
return;
14061324
}
14071325
}

lib/SILGen/SILGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
773773
/// application based on a main type and optionally a main type.
774774
void emitArtificialTopLevel(Decl *mainDecl);
775775

776+
/// Generate code for calling the given main function.
777+
void emitCallToMain(FuncDecl *mainDecl);
778+
776779
/// Generate code into @main for starting the async main on the main thread.
777780
void emitAsyncMainThreadStart(SILDeclRef entryPoint);
778781

lib/SILGen/SILGenTopLevel.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,140 @@ void SILGenModule::emitEntryPoint(SourceFile *SF, SILFunction *TopLevel) {
178178
emitLazyConformancesForFunction(&toplevel);
179179
}
180180

181+
/// Generate code for calling the given main function.
182+
void SILGenFunction::emitCallToMain(FuncDecl *mainFunc) {
183+
// This function is effectively emitting SIL for:
184+
// return try await TheType.$main();
185+
auto loc = SILLocation(mainFunc);
186+
auto *entryBlock = B.getInsertionBB();
187+
188+
SILDeclRef mainFunctionDeclRef(mainFunc, SILDeclRef::Kind::Func);
189+
SILFunction *mainFunction =
190+
SGM.getFunction(mainFunctionDeclRef, NotForDefinition);
191+
192+
NominalTypeDecl *mainType =
193+
mainFunc->getDeclContext()->getSelfNominalTypeDecl();
194+
auto metatype = B.createMetatype(mainType, getLoweredType(mainType->getInterfaceType()));
195+
196+
auto mainFunctionRef = B.createFunctionRef(loc, mainFunction);
197+
198+
auto builtinInt32Type = SILType::getBuiltinIntegerType(
199+
32, getASTContext());
200+
201+
// Set up the exit block, which will either return the exit value
202+
// (for synchronous main()) or call exit() with the return value (for
203+
// asynchronous main()).
204+
auto *exitBlock = createBasicBlock();
205+
SILValue exitCode =
206+
exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None);
207+
B.setInsertionPoint(exitBlock);
208+
209+
if (!mainFunc->hasAsync()) {
210+
auto returnType = F.getConventions().getSingleSILResultType(
211+
B.getTypeExpansionContext());
212+
if (exitCode->getType() != returnType)
213+
exitCode = B.createStruct(loc, returnType, exitCode);
214+
B.createReturn(loc, exitCode);
215+
} else {
216+
FuncDecl *exitFuncDecl = SGM.getExit();
217+
assert(exitFuncDecl && "Failed to find exit function declaration");
218+
SILFunction *exitSILFunc = SGM.getFunction(
219+
SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/ true),
220+
NotForDefinition);
221+
222+
SILFunctionType &funcType =
223+
*exitSILFunc->getLoweredType().getAs<SILFunctionType>();
224+
SILType retType = SILType::getPrimitiveObjectType(
225+
funcType.getParameters().front().getInterfaceType());
226+
exitCode = B.createStruct(loc, retType, exitCode);
227+
SILValue exitCall = B.createFunctionRef(loc, exitSILFunc);
228+
B.createApply(loc, exitCall, {}, {exitCode});
229+
B.createUnreachable(loc);
230+
}
231+
232+
// Form a call to the main function.
233+
CanSILFunctionType mainFnType = mainFunction->getConventions().funcTy;
234+
ASTContext &ctx = getASTContext();
235+
if (mainFnType->hasErrorResult()) {
236+
auto *successBlock = createBasicBlock();
237+
B.setInsertionPoint(successBlock);
238+
successBlock->createPhiArgument(SGM.Types.getEmptyTupleType(),
239+
OwnershipKind::None);
240+
SILValue zeroReturnValue =
241+
B.createIntegerLiteral(loc, builtinInt32Type, 0);
242+
B.createBranch(loc, exitBlock, {zeroReturnValue});
243+
244+
SILResultInfo errorResult = mainFnType->getErrorResult();
245+
SILType errorType = errorResult.getSILStorageInterfaceType();
246+
247+
auto *failureBlock = createBasicBlock();
248+
B.setInsertionPoint(failureBlock);
249+
SILValue error;
250+
if (IndirectErrorResult) {
251+
error = IndirectErrorResult;
252+
} else {
253+
error = failureBlock->createPhiArgument(
254+
errorType, OwnershipKind::Owned);
255+
}
256+
257+
// Log the error.
258+
if (errorType.getASTType()->isErrorExistentialType()) {
259+
// Load the indirect error, if needed.
260+
if (IndirectErrorResult) {
261+
const TypeLowering &errorExistentialTL = getTypeLowering(errorType);
262+
263+
error = emitLoad(
264+
loc, IndirectErrorResult, errorExistentialTL, SGFContext(),
265+
IsTake).forward(*this);
266+
}
267+
268+
// Call the errorInMain entrypoint, which takes an existential
269+
// error.
270+
B.createBuiltin(loc, ctx.getIdentifier("errorInMain"),
271+
SGM.Types.getEmptyTupleType(), {}, {error});
272+
} else {
273+
// Call the _errorInMainTyped entrypoint, which handles
274+
// arbitrary error types.
275+
SILValue tmpBuffer;
276+
277+
FuncDecl *entrypoint = ctx.getErrorInMainTyped();
278+
auto genericSig = entrypoint->getGenericSignature();
279+
SubstitutionMap subMap = SubstitutionMap::get(
280+
genericSig, [&](SubstitutableType *dependentType) {
281+
return errorType.getASTType();
282+
}, LookUpConformanceInModule(getModule().getSwiftModule()));
283+
284+
// Generic errors are passed indirectly.
285+
if (!error->getType().isAddress()) {
286+
auto *tmp = B.createAllocStack(loc,
287+
error->getType().getObjectType(),
288+
llvm::None);
289+
emitSemanticStore(
290+
loc, error, tmp,
291+
getTypeLowering(tmp->getType()), IsInitialization);
292+
tmpBuffer = tmp;
293+
error = tmp;
294+
}
295+
296+
emitApplyOfLibraryIntrinsic(
297+
loc, entrypoint, subMap,
298+
{ ManagedValue::forForwardedRValue(*this, error) },
299+
SGFContext());
300+
}
301+
B.createUnreachable(loc);
302+
303+
B.setInsertionPoint(entryBlock);
304+
B.createTryApply(loc, mainFunctionRef, SubstitutionMap(),
305+
{metatype}, successBlock, failureBlock);
306+
} else {
307+
B.setInsertionPoint(entryBlock);
308+
B.createApply(loc, mainFunctionRef, SubstitutionMap(), {metatype});
309+
SILValue returnValue =
310+
B.createIntegerLiteral(loc, builtinInt32Type, 0);
311+
B.createBranch(loc, exitBlock, {returnValue});
312+
}
313+
}
314+
181315
void SILGenModule::emitEntryPoint(SourceFile *SF) {
182316
assert(!M.lookUpFunction(getASTContext().getEntryPointFunctionName()) &&
183317
"already emitted toplevel?!");

lib/Sema/TypeCheckAttr.cpp

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,47 +2749,36 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
27492749
ConstraintSystemFlags::IgnoreAsyncSyncMismatch);
27502750
ConstraintLocator *locator =
27512751
CS.getConstraintLocator({}, ConstraintLocator::Member);
2752+
auto throwsTypeVar = CS.createTypeVariable(locator, 0);
2753+
27522754
// Allowed main function types
2753-
// `() -> Void`
2754-
// `() async -> Void`
2755-
// `() throws -> Void`
2756-
// `() async throws -> Void`
2757-
// `@MainActor () -> Void`
2758-
// `@MainActor () async -> Void`
2759-
// `@MainActor () throws -> Void`
2760-
// `@MainActor () async throws -> Void`
2755+
// `() throws(E) -> Void`
2756+
// `() async throws(E) -> Void`
2757+
// `@MainActor () throws(E) -> Void`
2758+
// `@MainActor () async throws(E) -> Void`
27612759
{
2762-
llvm::SmallVector<Type, 8> mainTypes = {
2763-
2764-
FunctionType::get(/*params*/ {}, context.TheEmptyTupleType,
2765-
ASTExtInfo()),
2766-
FunctionType::get(
2767-
/*params*/ {}, context.TheEmptyTupleType,
2768-
ASTExtInfoBuilder().withAsync().build()),
2769-
2760+
llvm::SmallVector<Type, 4> mainTypes = {
27702761
FunctionType::get(/*params*/ {}, context.TheEmptyTupleType,
2771-
ASTExtInfoBuilder().withThrows().build()),
2762+
ASTExtInfoBuilder().withThrows(
2763+
true, throwsTypeVar
2764+
).build()),
27722765

27732766
FunctionType::get(
27742767
/*params*/ {}, context.TheEmptyTupleType,
2775-
ASTExtInfoBuilder().withAsync().withThrows().build())};
2768+
ASTExtInfoBuilder().withAsync()
2769+
.withThrows(true, throwsTypeVar).build())};
27762770

27772771
Type mainActor = context.getMainActorType();
27782772
if (mainActor) {
27792773
auto extInfo = ASTExtInfoBuilder().withIsolation(
2780-
FunctionTypeIsolation::forGlobalActor(mainActor));
2774+
FunctionTypeIsolation::forGlobalActor(mainActor))
2775+
.withThrows(true, throwsTypeVar);
27812776
mainTypes.push_back(FunctionType::get(
27822777
/*params*/ {}, context.TheEmptyTupleType,
27832778
extInfo.build()));
27842779
mainTypes.push_back(FunctionType::get(
27852780
/*params*/ {}, context.TheEmptyTupleType,
27862781
extInfo.withAsync().build()));
2787-
mainTypes.push_back(FunctionType::get(
2788-
/*params*/ {}, context.TheEmptyTupleType,
2789-
extInfo.withThrows().build()));
2790-
mainTypes.push_back(FunctionType::get(
2791-
/*params*/ {}, context.TheEmptyTupleType,
2792-
extInfo.withAsync().withThrows().build()));
27932782
}
27942783
TypeVariableType *mainType =
27952784
CS.createTypeVariable(locator, /*options=*/0);

stdlib/public/core/ErrorType.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,16 @@ public func _errorInMain(_ error: Error) {
232232
#endif
233233
}
234234

235+
/// Invoked by the compiler when code at top level throws an uncaught, typed error.
236+
@_alwaysEmitIntoClient
237+
public func _errorInMainTyped<Failure: Error>(_ error: Failure) -> Never {
238+
#if !$Embedded
239+
fatalError("Error raised at top level: \(String(reflecting: error))")
240+
#else
241+
Builtin.int_trap()
242+
#endif
243+
}
244+
235245
/// Runtime function to determine the default code for an Error-conforming type.
236246
/// Called by the Foundation overlay.
237247
@_silgen_name("_swift_stdlib_getDefaultErrorCode")

0 commit comments

Comments
 (0)