// #target: macos-x64
module test;
import std;
import libc;

struct Doc { Head *head; }
struct Head { char[]* title; }

struct Summary
{
	char[]* title;
	bool ok;
}

fn void Summary.print(Summary *s, CFile out)
{
	// We don't have a native printf in C3 yet, so use libc,
	// which is not all that nice for the strings but...
	char[] title = s.title ? *s.title : "missing";
	libc::fprintf(out, "Summary({ .title = %.*s, .ok = %s})", (int)title.len, title.ptr, s.ok ? (char*)"true" : (char*)"false");
}

fn bool contains(char[] haystack, char[] needle)
{
	usz len = haystack.len;
	usz needle_len = needle.len;
	if (len < needle_len) return false;
	if (!needle_len) return true;
	len -= needle_len - 1;
	for (usz i = 0; i < len; i++)
	{
		if (libc::memcmp(&haystack[i], needle.ptr, needle_len) == 0)
		{
			return true;
		}
	}
	return false;
}

macro dupe(value)
{
	$typeof(&value) temp = malloc($sizeof(value));
	if (!temp) return OUT_OF_MEMORY?;
	*temp = value;
	return temp;
}

faultdef BAD_READ, OUT_OF_MEMORY;

fn Doc? readDoc(char[] url)
{
	if (contains(url, "fail")) return BAD_READ?;
	if (contains(url, "head-missing")) return { .head = null };
	if (contains(url, "title-missing")) return { dupe((Head) { .title = null })! };
	if (contains(url, "title-empty")) return { dupe((Head) { .title = dupe((char[])"")! })! };
	// Not particularly elegant due to missing string functions.
	isz len = libc::snprintf(null, 0, "Title of %.*s", (int)url.len, url.ptr);
	char* str = malloc(len + 1);
	if (!str) return OUT_OF_MEMORY?;
	libc::snprintf(str, len + 1, "Title of %.*s", (int)url.len, url.ptr);
	return { dupe((Head) { .title = dupe(str[..len - 1])! })! };
}

fn Summary buildSummary(Doc doc)
{
    return {
        .title = doc.head ? doc.head.title : null,
        .ok = true,
    };
}

fn Summary readAndBuildSummary(char[] url)
{
	return buildSummary(readDoc(url)) ?? (Summary) { .title = null, .ok = false };
	/*
	// or
	Summary summary = buildSummary(readDoc(url));
	if (catch summary) return Summary { .title = null, .ok = false };
	return summary;
	// or
	Summary summary = buildSummary(readDoc(url));
	if (try summary) return summary;
	return Summary { .title = null, .ok = false };
	*/
}


faultdef  TITLE_MISSING;

fn bool? isTitleNonEmpty(Doc doc)
{
	if (!doc.head) return TITLE_MISSING?;
	char[]* head = doc.head.title;
	if (!head) return TITLE_MISSING?;
	return (*head).len > 0;
}


fn bool? readWhetherTitleNonEmpty(char[] url)
{
    return isTitleNonEmpty(readDoc(url));
}

fn char* bool_to_string(bool b)
{
	return b ? "true" : "false";
}
fn char* nameFromError(fault e)
{
	switch (e)
	{
		case TITLE_MISSING:
			return "no title";
		case BAD_READ:
			return "bad read";
		case OUT_OF_MEMORY:
			return "out of memory";
		default:
			return "unknown error";
	}
}


fn void main()
{
    const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" };
    foreach (char[] url : URLS)
    {
        // Yes, it's pretty onerous to print strings for the moment in C3
        libc::printf(`Checking "https://%.*s/":` "\n", (int)url.len, url.ptr);
        Summary summary = readAndBuildSummary(url);
        libc::printf("  Summary: ");
        summary.print(libc::stdout());
        libc::printf("\n");
        char[] title_sure = summary.title ? *summary.title : "";
        libc::printf("  Title: %.*s\n", (int)title_sure.len, title_sure.ptr);
        bool? has_title = readWhetherTitleNonEmpty(url);
        // This looks a bit less than elegant, but as you see it's mostly due to having to
        // use printf here.
        libc::printf("  Has title: %s vs %s\n", bool_to_string(has_title) ?? nameFromError(@catch(has_title)), (has_title ?? false) ? (char*)"true" : (char*)"false");
    }
}

/* #expect: test.ll

define void @test.Summary.print(ptr %0, ptr %1) #0 {
entry:
  %title = alloca %"char[]", align 8
  %2 = load ptr, ptr %0, align 8
  %i2b = icmp ne ptr %2, null
  br i1 %i2b, label %cond.lhs, label %cond.rhs

cond.lhs:                                         ; preds = %entry
  %3 = load ptr, ptr %0, align 8
  %4 = load %"char[]", ptr %3, align 8
  br label %cond.phi

cond.rhs:                                         ; preds = %entry
  br label %cond.phi

cond.phi:                                         ; preds = %cond.rhs, %cond.lhs
  %val = phi %"char[]" [ %4, %cond.lhs ], [ { ptr @.str.26, i64 7 }, %cond.rhs ]
  store %"char[]" %val, ptr %title, align 8
  %ptradd = getelementptr inbounds i8, ptr %title, i64 8
  %5 = load i64, ptr %ptradd, align 8
  %trunc = trunc i64 %5 to i32
  %ptradd1 = getelementptr inbounds i8, ptr %0, i64 8
  %6 = load i8, ptr %ptradd1, align 8
  %7 = trunc i8 %6 to i1
  %ternary = select i1 %7, ptr @.str.28, ptr @.str.29
  %8 = load ptr, ptr %title, align 8
  %9 = call i32 (ptr, ptr, ...) @fprintf(ptr %1, ptr @.str.27, i32 %trunc, ptr %8, ptr %ternary)
  ret void
}

define zeroext i8 @test.contains(ptr %0, i64 %1, ptr %2, i64 %3) #0 {
entry:
  %haystack = alloca %"char[]", align 8
  %needle = alloca %"char[]", align 8
  %len = alloca i64, align 8
  %needle_len = alloca i64, align 8
  %i = alloca i64, align 8
  store ptr %0, ptr %haystack, align 8
  %ptradd = getelementptr inbounds i8, ptr %haystack, i64 8
  store i64 %1, ptr %ptradd, align 8
  store ptr %2, ptr %needle, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %needle, i64 8
  store i64 %3, ptr %ptradd1, align 8
  %ptradd2 = getelementptr inbounds i8, ptr %haystack, i64 8
  %4 = load i64, ptr %ptradd2, align 8
  store i64 %4, ptr %len, align 8
  %ptradd3 = getelementptr inbounds i8, ptr %needle, i64 8
  %5 = load i64, ptr %ptradd3, align 8
  store i64 %5, ptr %needle_len, align 8
  %6 = load i64, ptr %len, align 8
  %7 = load i64, ptr %needle_len, align 8
  %lt = icmp ult i64 %6, %7
  br i1 %lt, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  ret i8 0

if.exit:                                          ; preds = %entry
  %8 = load i64, ptr %needle_len, align 8
  %i2nb = icmp eq i64 %8, 0
  br i1 %i2nb, label %if.then4, label %if.exit5

if.then4:                                         ; preds = %if.exit
  ret i8 1

if.exit5:                                         ; preds = %if.exit
  %9 = load i64, ptr %len, align 8
  %10 = load i64, ptr %needle_len, align 8
  %sub = sub i64 %10, 1
  %sub6 = sub i64 %9, %sub
  store i64 %sub6, ptr %len, align 8
  store i64 0, ptr %i, align 8
  br label %loop.cond

loop.cond:                                        ; preds = %if.exit10, %if.exit5
  %11 = load i64, ptr %i, align 8
  %12 = load i64, ptr %len, align 8
  %lt7 = icmp ult i64 %11, %12
  br i1 %lt7, label %loop.body, label %loop.exit

loop.body:                                        ; preds = %loop.cond
  %13 = load ptr, ptr %haystack, align 8
  %14 = load i64, ptr %i, align 8
  %ptradd8 = getelementptr inbounds i8, ptr %13, i64 %14
  %15 = load ptr, ptr %needle, align 8
  %16 = load i64, ptr %needle_len, align 8
  %17 = call i32 @memcmp(ptr %ptradd8, ptr %15, i64 %16)
  %eq = icmp eq i32 %17, 0
  br i1 %eq, label %if.then9, label %if.exit10

if.then9:                                         ; preds = %loop.body
  ret i8 1

if.exit10:                                        ; preds = %loop.body
  %18 = load i64, ptr %i, align 8
  %add = add i64 %18, 1
  store i64 %add, ptr %i, align 8
  br label %loop.cond

loop.exit:                                        ; preds = %loop.cond
  ret i8 0
}

define i64 @test.readDoc(ptr %0, ptr %1, i64 %2) #0 {
entry:
  %url = alloca %"char[]", align 8
  %reterr = alloca i64, align 8
  %literal = alloca %Doc, align 8
  %reterr11 = alloca i64, align 8
  %literal12 = alloca %Doc, align 8
  %error_var = alloca i64, align 8
  %literal13 = alloca %Head, align 8
  %value = alloca %Head, align 8
  %temp = alloca ptr, align 8
  %reterr21 = alloca i64, align 8
  %literal22 = alloca %Doc, align 8
  %error_var23 = alloca i64, align 8
  %literal24 = alloca %Head, align 8
  %error_var25 = alloca i64, align 8
  %value26 = alloca %"char[]", align 8
  %temp27 = alloca ptr, align 8
  %value33 = alloca %Head, align 8
  %temp34 = alloca ptr, align 8
  %len = alloca i64, align 8
  %str = alloca ptr, align 8
  %reterr48 = alloca i64, align 8
  %literal49 = alloca %Doc, align 8
  %error_var50 = alloca i64, align 8
  %literal51 = alloca %Head, align 8
  %error_var52 = alloca i64, align 8
  %value53 = alloca %"char[]", align 8
  %temp54 = alloca ptr, align 8
  %value60 = alloca %Head, align 8
  %temp61 = alloca ptr, align 8
  store ptr %1, ptr %url, align 8
  %ptradd = getelementptr inbounds i8, ptr %url, i64 8
  store i64 %2, ptr %ptradd, align 8
  %lo = load ptr, ptr %url, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %url, i64 8
  %hi = load i64, ptr %ptradd1, align 8
  %3 = call i8 @test.contains(ptr %lo, i64 %hi, ptr @.str, i64 4)
  %4 = trunc i8 %3 to i1
  br i1 %4, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  ret i64 ptrtoint (ptr @test.BAD_READ to i64)

if.exit:                                          ; preds = %entry
  %lo2 = load ptr, ptr %url, align 8
  %ptradd3 = getelementptr inbounds i8, ptr %url, i64 8
  %hi4 = load i64, ptr %ptradd3, align 8
  %5 = call i8 @test.contains(ptr %lo2, i64 %hi4, ptr @.str.1, i64 12)
  %6 = trunc i8 %5 to i1
  br i1 %6, label %if.then5, label %if.exit6

if.then5:                                         ; preds = %if.exit
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const, i32 8, i1 false)
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %0, ptr align 8 %literal, i32 8, i1 false)
  ret i64 0

if.exit6:                                         ; preds = %if.exit
  %lo7 = load ptr, ptr %url, align 8
  %ptradd8 = getelementptr inbounds i8, ptr %url, i64 8
  %hi9 = load i64, ptr %ptradd8, align 8
  %7 = call i8 @test.contains(ptr %lo7, i64 %hi9, ptr @.str.2, i64 13)
  %8 = trunc i8 %7 to i1
  br i1 %8, label %if.then10, label %if.exit16

if.then10:                                        ; preds = %if.exit6
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal13, ptr align 8 @.__const.3, i32 8, i1 false)
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %value, ptr align 8 %literal13, i32 8, i1 false)
  %9 = call ptr @std.core.mem.malloc(i64 8) #3
  store ptr %9, ptr %temp, align 8
  %10 = load ptr, ptr %temp, align 8
  %i2nb = icmp eq ptr %10, null
  br i1 %i2nb, label %if.then14, label %if.exit15

if.then14:                                        ; preds = %if.then10
  store i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64), ptr %error_var, align 8
  br label %guard_block

if.exit15:                                        ; preds = %if.then10
  %11 = load ptr, ptr %temp, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %11, ptr align 8 %value, i32 8, i1 false)
  br label %noerr_block

guard_block:                                      ; preds = %if.then14
  %12 = load i64, ptr %error_var, align 8
  ret i64 %12

noerr_block:                                      ; preds = %if.exit15
  %13 = load ptr, ptr %temp, align 8
  store ptr %13, ptr %literal12, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %0, ptr align 8 %literal12, i32 8, i1 false)
  ret i64 0

if.exit16:                                        ; preds = %if.exit6
  %lo17 = load ptr, ptr %url, align 8
  %ptradd18 = getelementptr inbounds i8, ptr %url, i64 8
  %hi19 = load i64, ptr %ptradd18, align 8
  %14 = call i8 @test.contains(ptr %lo17, i64 %hi19, ptr @.str.4, i64 11)
  %15 = trunc i8 %14 to i1
  br i1 %15, label %if.then20, label %if.exit40

if.then20:                                        ; preds = %if.exit16
  store ptr null, ptr %literal24, align 8
  store %"char[]" { ptr @.emptystr, i64 0 }, ptr %value26, align 8
  %16 = call ptr @std.core.mem.malloc(i64 16) #3
  store ptr %16, ptr %temp27, align 8
  %17 = load ptr, ptr %temp27, align 8
  %i2nb28 = icmp eq ptr %17, null
  br i1 %i2nb28, label %if.then29, label %if.exit30

if.then29:                                        ; preds = %if.then20
  store i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64), ptr %error_var25, align 8
  br label %guard_block31

if.exit30:                                        ; preds = %if.then20
  %18 = load ptr, ptr %temp27, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %18, ptr align 8 %value26, i32 16, i1 false)
  br label %noerr_block32

guard_block31:                                    ; preds = %if.then29
  %19 = load i64, ptr %error_var25, align 8
  ret i64 %19

noerr_block32:                                    ; preds = %if.exit30
  %20 = load ptr, ptr %temp27, align 8
  store ptr %20, ptr %literal24, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %value33, ptr align 8 %literal24, i32 8, i1 false)
  %21 = call ptr @std.core.mem.malloc(i64 8) #3
  store ptr %21, ptr %temp34, align 8
  %22 = load ptr, ptr %temp34, align 8
  %i2nb35 = icmp eq ptr %22, null
  br i1 %i2nb35, label %if.then36, label %if.exit37

if.then36:                                        ; preds = %noerr_block32
  store i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64), ptr %error_var23, align 8
  br label %guard_block38

if.exit37:                                        ; preds = %noerr_block32
  %23 = load ptr, ptr %temp34, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %23, ptr align 8 %value33, i32 8, i1 false)
  br label %noerr_block39

guard_block38:                                    ; preds = %if.then36
  %24 = load i64, ptr %error_var23, align 8
  ret i64 %24

noerr_block39:                                    ; preds = %if.exit37
  %25 = load ptr, ptr %temp34, align 8
  store ptr %25, ptr %literal22, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %0, ptr align 8 %literal22, i32 8, i1 false)
  ret i64 0

if.exit40:                                        ; preds = %if.exit16
  %ptradd41 = getelementptr inbounds i8, ptr %url, i64 8
  %26 = load i64, ptr %ptradd41, align 8
  %trunc = trunc i64 %26 to i32
  %27 = load ptr, ptr %url, align 8
  %28 = call i32 (ptr, i64, ptr, ...) @snprintf(ptr null, i64 0, ptr @.str.5, i32 %trunc, ptr %27)
  %sext = sext i32 %28 to i64
  store i64 %sext, ptr %len, align 8
  %29 = load i64, ptr %len, align 8
  %add = add i64 %29, 1
  %30 = call ptr @std.core.mem.malloc(i64 %add) #3
  store ptr %30, ptr %str, align 8
  %31 = load ptr, ptr %str, align 8
  %i2nb42 = icmp eq ptr %31, null
  br i1 %i2nb42, label %if.then43, label %if.exit44

if.then43:                                        ; preds = %if.exit40
  ret i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64)

if.exit44:                                        ; preds = %if.exit40
  %32 = load i64, ptr %len, align 8
  %add45 = add i64 %32, 1
  %ptradd46 = getelementptr inbounds i8, ptr %url, i64 8
  %33 = load i64, ptr %ptradd46, align 8
  %trunc47 = trunc i64 %33 to i32
  %34 = load ptr, ptr %str, align 8
  %35 = load ptr, ptr %url, align 8
  %36 = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %34, i64 %add45, ptr @.str.6, i32 %trunc47, ptr %35)
  store ptr null, ptr %literal51, align 8
  %37 = load ptr, ptr %str, align 8
  %38 = load i64, ptr %len, align 8
  %sub = sub i64 %38, 1
  %39 = add i64 %sub, 1
  %size = sub i64 %39, 0
  %40 = insertvalue %"char[]" undef, ptr %37, 0
  %41 = insertvalue %"char[]" %40, i64 %size, 1
  store %"char[]" %41, ptr %value53, align 8
  %42 = call ptr @std.core.mem.malloc(i64 16) #3
  store ptr %42, ptr %temp54, align 8
  %43 = load ptr, ptr %temp54, align 8
  %i2nb55 = icmp eq ptr %43, null
  br i1 %i2nb55, label %if.then56, label %if.exit57

if.then56:                                        ; preds = %if.exit44
  store i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64), ptr %error_var52, align 8
  br label %guard_block58

if.exit57:                                        ; preds = %if.exit44
  %44 = load ptr, ptr %temp54, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %44, ptr align 8 %value53, i32 16, i1 false)
  br label %noerr_block59

guard_block58:                                    ; preds = %if.then56
  %45 = load i64, ptr %error_var52, align 8
  ret i64 %45

noerr_block59:                                    ; preds = %if.exit57
  %46 = load ptr, ptr %temp54, align 8
  store ptr %46, ptr %literal51, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %value60, ptr align 8 %literal51, i32 8, i1 false)
  %47 = call ptr @std.core.mem.malloc(i64 8) #3
  store ptr %47, ptr %temp61, align 8
  %48 = load ptr, ptr %temp61, align 8
  %i2nb62 = icmp eq ptr %48, null
  br i1 %i2nb62, label %if.then63, label %if.exit64

if.then63:                                        ; preds = %noerr_block59
  store i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64), ptr %error_var50, align 8
  br label %guard_block65

if.exit64:                                        ; preds = %noerr_block59
  %49 = load ptr, ptr %temp61, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %49, ptr align 8 %value60, i32 8, i1 false)
  br label %noerr_block66

guard_block65:                                    ; preds = %if.then63
  %50 = load i64, ptr %error_var50, align 8
  ret i64 %50

noerr_block66:                                    ; preds = %if.exit64
  %51 = load ptr, ptr %temp61, align 8
  store ptr %51, ptr %literal49, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %0, ptr align 8 %literal49, i32 8, i1 false)
  ret i64 0
}

define { ptr, i8 } @test.buildSummary(ptr %0) #0 {
entry:
  %doc = alloca %Doc, align 8
  %literal = alloca %Summary, align 8
  store ptr %0, ptr %doc, align 8
  store ptr null, ptr %literal, align 8
  %ptradd = getelementptr inbounds i8, ptr %literal, i64 8
  store i8 0, ptr %ptradd, align 8
  %1 = load ptr, ptr %doc, align 8
  %i2b = icmp ne ptr %1, null
  br i1 %i2b, label %cond.lhs, label %cond.rhs

cond.lhs:                                         ; preds = %entry
  %2 = load ptr, ptr %doc, align 8
  %3 = load ptr, ptr %2, align 8
  br label %cond.phi

cond.rhs:                                         ; preds = %entry
  br label %cond.phi

cond.phi:                                         ; preds = %cond.rhs, %cond.lhs
  %val = phi ptr [ %3, %cond.lhs ], [ null, %cond.rhs ]
  store ptr %val, ptr %literal, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %literal, i64 8
  store i8 1, ptr %ptradd1, align 8
  %4 = load { ptr, i8 }, ptr %literal, align 8
  ret { ptr, i8 } %4
}

define { ptr, i8 } @test.readAndBuildSummary(ptr %0, i64 %1) #0 {
entry:
  %url = alloca %"char[]", align 8
  %retparam = alloca %Doc, align 8
  %result = alloca %Summary, align 8
  %literal = alloca %Summary, align 8
  store ptr %0, ptr %url, align 8
  %ptradd = getelementptr inbounds i8, ptr %url, i64 8
  store i64 %1, ptr %ptradd, align 8
  %lo = load ptr, ptr %url, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %url, i64 8
  %hi = load i64, ptr %ptradd1, align 8
  %2 = call i64 @test.readDoc(ptr %retparam, ptr %lo, i64 %hi)
  %not_err = icmp eq i64 %2, 0
  %3 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
  br i1 %3, label %after_check, label %else_block

after_check:                                      ; preds = %entry
  %4 = load ptr, ptr %retparam, align 8
  %5 = call { ptr, i8 } @test.buildSummary(ptr %4)
  store { ptr, i8 } %5, ptr %result, align 8
  br label %phi_block

else_block:                                       ; preds = %entry
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.7, i32 16, i1 false)
  br label %phi_block

phi_block:                                        ; preds = %else_block, %after_check
  %val = phi ptr [ %result, %after_check ], [ %literal, %else_block ]
  %6 = load { ptr, i8 }, ptr %val, align 8
  ret { ptr, i8 } %6
}

define i64 @test.isTitleNonEmpty(ptr %0, ptr %1) #0 {
entry:
  %doc = alloca %Doc, align 8
  %head = alloca ptr, align 8
  %reterr = alloca i64, align 8
  store ptr %1, ptr %doc, align 8
  %2 = load ptr, ptr %doc, align 8
  %i2nb = icmp eq ptr %2, null
  br i1 %i2nb, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  ret i64 ptrtoint (ptr @test.TITLE_MISSING to i64)

if.exit:                                          ; preds = %entry
  %3 = load ptr, ptr %doc, align 8
  %4 = load ptr, ptr %3, align 8
  store ptr %4, ptr %head, align 8
  %5 = load ptr, ptr %head, align 8
  %i2nb1 = icmp eq ptr %5, null
  br i1 %i2nb1, label %if.then2, label %if.exit3

if.then2:                                         ; preds = %if.exit
  ret i64 ptrtoint (ptr @test.TITLE_MISSING to i64)

if.exit3:                                         ; preds = %if.exit
  %6 = load ptr, ptr %head, align 8
  %ptradd = getelementptr inbounds i8, ptr %6, i64 8
  %7 = load i64, ptr %ptradd, align 8
  %lt = icmp ult i64 0, %7
  %8 = zext i1 %lt to i8
  store i8 %8, ptr %0, align 1
  ret i64 0
}


define i64 @test.readWhetherTitleNonEmpty(ptr %0, ptr %1, i64 %2) #0 {
entry:
  %url = alloca %"char[]", align 8
  %reterr = alloca i64, align 8
  %retparam = alloca %Doc, align 8
  %retparam2 = alloca i8, align 1
  store ptr %1, ptr %url, align 8
  %ptradd = getelementptr inbounds i8, ptr %url, i64 8
  store i64 %2, ptr %ptradd, align 8
  %lo = load ptr, ptr %url, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %url, i64 8
  %hi = load i64, ptr %ptradd1, align 8
  %3 = call i64 @test.readDoc(ptr %retparam, ptr %lo, i64 %hi)
  %not_err = icmp eq i64 %3, 0
  %4 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
  br i1 %4, label %after_check, label %assign_optional

assign_optional:                                  ; preds = %entry
  store i64 %3, ptr %reterr, align 8
  br label %err_retblock

after_check:                                      ; preds = %entry
  %5 = load ptr, ptr %retparam, align 8
  %6 = call i64 @test.isTitleNonEmpty(ptr %retparam2, ptr %5)
  %not_err3 = icmp eq i64 %6, 0
  %7 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true)
  br i1 %7, label %after_check5, label %assign_optional4

assign_optional4:                                 ; preds = %after_check
  store i64 %6, ptr %reterr, align 8
  br label %err_retblock

after_check5:                                     ; preds = %after_check
  %8 = load i8, ptr %retparam2, align 1
  store i8 %8, ptr %0, align 1
  ret i64 0

err_retblock:                                     ; preds = %assign_optional4, %assign_optional
  %9 = load i64, ptr %reterr, align 8
  ret i64 %9
}


define ptr @test.bool_to_string(i8 zeroext %0) #0 {
entry:
  %1 = trunc i8 %0 to i1
  %ternary = select i1 %1, ptr @.str.8, ptr @.str.9
  ret ptr %ternary
}


define ptr @test.nameFromError(i64 %0) #0 {
entry:
  %switch = alloca i64, align 8
  store i64 %0, ptr %switch, align 8
  br label %switch.entry

switch.entry:                                     ; preds = %entry
  %1 = load i64, ptr %switch, align 8
  %eq = icmp eq i64 ptrtoint (ptr @test.TITLE_MISSING to i64), %1
  br i1 %eq, label %switch.case, label %next_if

switch.case:                                      ; preds = %switch.entry
  ret ptr @.str.10

next_if:                                          ; preds = %switch.entry
  %eq1 = icmp eq i64 ptrtoint (ptr @test.BAD_READ to i64), %1
  br i1 %eq1, label %switch.case2, label %next_if3

switch.case2:                                     ; preds = %next_if
  ret ptr @.str.11

next_if3:                                         ; preds = %next_if
  %eq4 = icmp eq i64 ptrtoint (ptr @test.OUT_OF_MEMORY to i64), %1
  br i1 %eq4, label %switch.case5, label %next_if6

switch.case5:                                     ; preds = %next_if3
  ret ptr @.str.12

next_if6:                                         ; preds = %next_if3
  br label %switch.default

switch.default:                                   ; preds = %next_if6
  ret ptr @.str.13
}


define void @test.main() #0 {
entry:
  %.anon = alloca i64, align 8
  %url = alloca %"char[]", align 8
  %summary = alloca %Summary, align 8
  %result = alloca %Summary, align 8
  %title_sure = alloca %"char[]", align 8
  %has_title = alloca i8, align 1
  %has_title.f = alloca i64, align 8
  %retparam = alloca i8, align 1
  %blockret = alloca i64, align 8
  %f = alloca i64, align 8
  %0 = load i64, ptr getelementptr inbounds (i8, ptr @main.URLS, i64 8), align 8
  store i64 0, ptr %.anon, align 8
  br label %loop.cond

loop.cond:                                        ; preds = %phi_block19, %entry
  %1 = load i64, ptr %.anon, align 8
  %lt = icmp ult i64 %1, %0
  br i1 %lt, label %loop.body, label %loop.exit

loop.body:                                        ; preds = %loop.cond
  %2 = load ptr, ptr @main.URLS, align 8
  %3 = load i64, ptr %.anon, align 8
  %ptroffset = getelementptr inbounds [16 x i8], ptr %2, i64 %3
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %url, ptr align 8 %ptroffset, i32 16, i1 false)
  %ptradd = getelementptr inbounds i8, ptr %url, i64 8
  %4 = load i64, ptr %ptradd, align 8
  %trunc = trunc i64 %4 to i32
  %5 = load ptr, ptr %url, align 8
  %6 = call i32 (ptr, ...) @printf(ptr @.str.19, i32 %trunc, ptr %5)
  %lo = load ptr, ptr %url, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %url, i64 8
  %hi = load i64, ptr %ptradd1, align 8
  %7 = call { ptr, i8 } @test.readAndBuildSummary(ptr %lo, i64 %hi)
  store { ptr, i8 } %7, ptr %result, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %summary, ptr align 8 %result, i32 16, i1 false)
  %8 = call i32 (ptr, ...) @printf(ptr @.str.20)
  %9 = load ptr, ptr @__stdoutp, align 8
  call void @test.Summary.print(ptr %summary, ptr %9)
  %10 = call i32 (ptr, ...) @printf(ptr @.str.21)
  %11 = load ptr, ptr %summary, align 8
  %i2b = icmp ne ptr %11, null
  br i1 %i2b, label %cond.lhs, label %cond.rhs

cond.lhs:                                         ; preds = %loop.body
  %12 = load ptr, ptr %summary, align 8
  %13 = load %"char[]", ptr %12, align 8
  br label %cond.phi

cond.rhs:                                         ; preds = %loop.body
  br label %cond.phi

cond.phi:                                         ; preds = %cond.rhs, %cond.lhs
  %val = phi %"char[]" [ %13, %cond.lhs ], [ { ptr @.emptystr, i64 0 }, %cond.rhs ]
  store %"char[]" %val, ptr %title_sure, align 8
  %ptradd2 = getelementptr inbounds i8, ptr %title_sure, i64 8
  %14 = load i64, ptr %ptradd2, align 8
  %trunc3 = trunc i64 %14 to i32
  %15 = load ptr, ptr %title_sure, align 8
  %16 = call i32 (ptr, ...) @printf(ptr @.str.22, i32 %trunc3, ptr %15)
  %lo4 = load ptr, ptr %url, align 8
  %ptradd5 = getelementptr inbounds i8, ptr %url, i64 8
  %hi6 = load i64, ptr %ptradd5, align 8
  %17 = call i64 @test.readWhetherTitleNonEmpty(ptr %retparam, ptr %lo4, i64 %hi6)
  %not_err = icmp eq i64 %17, 0
  %18 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
  br i1 %18, label %after_check, label %assign_optional

assign_optional:                                  ; preds = %cond.phi
  store i64 %17, ptr %has_title.f, align 8
  br label %after_assign

after_check:                                      ; preds = %cond.phi
  %19 = load i8, ptr %retparam, align 1
  store i8 %19, ptr %has_title, align 1
  store i64 0, ptr %has_title.f, align 8
  br label %after_assign

after_assign:                                     ; preds = %after_check, %assign_optional
  %optval = load i64, ptr %has_title.f, align 8
  %not_err7 = icmp eq i64 %optval, 0
  %20 = call i1 @llvm.expect.i1(i1 %not_err7, i1 true)
  br i1 %20, label %after_check8, label %else_block

after_check8:                                     ; preds = %after_assign
  %21 = load i8, ptr %has_title, align 1
  %22 = call ptr @test.bool_to_string(i8 zeroext %21)
  br label %phi_block

else_block:                                       ; preds = %after_assign
  br label %testblock

testblock:                                        ; preds = %else_block
  %optval9 = load i64, ptr %has_title.f, align 8
  %not_err10 = icmp eq i64 %optval9, 0
  %23 = call i1 @llvm.expect.i1(i1 %not_err10, i1 true)
  br i1 %23, label %after_check12, label %assign_optional11

assign_optional11:                                ; preds = %testblock
  store i64 %optval9, ptr %f, align 8
  br label %end_block

after_check12:                                    ; preds = %testblock
  store i64 0, ptr %f, align 8
  br label %end_block

end_block:                                        ; preds = %after_check12, %assign_optional11
  %24 = load i64, ptr %f, align 8
  %i2b13 = icmp ne i64 %24, 0
  br i1 %i2b13, label %if.then, label %if.exit

if.then:                                          ; preds = %end_block
  %25 = load i64, ptr %f, align 8
  store i64 %25, ptr %blockret, align 8
  br label %expr_block.exit

if.exit:                                          ; preds = %end_block
  store i64 0, ptr %blockret, align 8
  br label %expr_block.exit

expr_block.exit:                                  ; preds = %if.exit, %if.then
  %26 = load i64, ptr %blockret, align 8
  %27 = call ptr @test.nameFromError(i64 %26)
  br label %phi_block

phi_block:                                        ; preds = %expr_block.exit, %after_check8
  %val14 = phi ptr [ %22, %after_check8 ], [ %27, %expr_block.exit ]
  %optval15 = load i64, ptr %has_title.f, align 8
  %not_err16 = icmp eq i64 %optval15, 0
  %28 = call i1 @llvm.expect.i1(i1 %not_err16, i1 true)
  br i1 %28, label %after_check17, label %else_block18

after_check17:                                    ; preds = %phi_block
  %29 = load i8, ptr %has_title, align 1
  %30 = trunc i8 %29 to i1
  br label %phi_block19

else_block18:                                     ; preds = %phi_block
  br label %phi_block19

phi_block19:                                      ; preds = %else_block18, %after_check17
  %val20 = phi i1 [ %30, %after_check17 ], [ false, %else_block18 ]
  %ternary = select i1 %val20, ptr @.str.24, ptr @.str.25
  %31 = call i32 (ptr, ...) @printf(ptr @.str.23, ptr %val14, ptr %ternary)
  %32 = load i64, ptr %.anon, align 8
  %addnuw = add nuw i64 %32, 1
  store i64 %addnuw, ptr %.anon, align 8
  br label %loop.cond

loop.exit:                                        ; preds = %loop.cond
  ret void
}
