Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const root = @import("@build");
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
Expand All @@ -10,6 +9,7 @@ const ArrayList = std.ArrayList;
const File = std.fs.File;
const Step = std.Build.Step;

pub const root = @import("@build");
pub const dependencies = @import("@dependencies");

pub fn main() !void {
Expand Down
72 changes: 67 additions & 5 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ named_writefiles: std.StringArrayHashMap(*Step.WriteFile),
/// A map from build root dirs to the corresponding `*Dependency`. This is shared with all child
/// `Build`s.
initialized_deps: *InitializedDepMap,
/// The hash of this instance's package. `""` means that this is the root package.
pkg_hash: []const u8,
/// A mapping from dependency names to package hashes.
available_deps: AvailableDeps,

Expand Down Expand Up @@ -305,6 +307,7 @@ pub fn create(
.modules = std.StringArrayHashMap(*Module).init(arena),
.named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(arena),
.initialized_deps = initialized_deps,
.pkg_hash = "",
.available_deps = available_deps,
.release_mode = .off,
};
Expand All @@ -318,10 +321,11 @@ fn createChild(
parent: *Build,
dep_name: []const u8,
build_root: Cache.Directory,
pkg_hash: []const u8,
pkg_deps: AvailableDeps,
user_input_options: UserInputOptionsMap,
) !*Build {
const child = try createChildOnly(parent, dep_name, build_root, pkg_deps, user_input_options);
const child = try createChildOnly(parent, dep_name, build_root, pkg_hash, pkg_deps, user_input_options);
try determineAndApplyInstallPrefix(child);
return child;
}
Expand All @@ -330,6 +334,7 @@ fn createChildOnly(
parent: *Build,
dep_name: []const u8,
build_root: Cache.Directory,
pkg_hash: []const u8,
pkg_deps: AvailableDeps,
user_input_options: UserInputOptionsMap,
) !*Build {
Expand Down Expand Up @@ -397,6 +402,7 @@ fn createChildOnly(
.modules = std.StringArrayHashMap(*Module).init(allocator),
.named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(allocator),
.initialized_deps = parent.initialized_deps,
.pkg_hash = pkg_hash,
.available_deps = pkg_deps,
.release_mode = parent.release_mode,
};
Expand Down Expand Up @@ -1830,6 +1836,26 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 {
std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ name, full_path });
}

inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, comptime dep_name: []const u8) []const u8 {
const build_runner = @import("root");
const deps = build_runner.dependencies;

const b_pkg_hash, const b_pkg_deps = comptime for (@typeInfo(deps.packages).Struct.decls) |decl| {
const pkg_hash = decl.name;
const pkg = @field(deps.packages, pkg_hash);
if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
} else .{ "", deps.root_deps };
if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {
std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ asking_build_zig, b.pathFromRoot("build.zig") });
}
comptime for (b_pkg_deps) |dep| {
if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];
};

const full_path = b.pathFromRoot("build.zig.zon");
std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ dep_name, full_path });
}

fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void {
b.graph.needed_lazy_dependencies.put(b.graph.arena, pkg_hash, {}) catch @panic("OOM");
}
Expand Down Expand Up @@ -1860,7 +1886,7 @@ pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
markNeededLazyDep(b, pkg_hash);
return null;
}
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
}
}

Expand All @@ -1878,13 +1904,48 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
if (@hasDecl(pkg, "available")) {
std.debug.panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name });
}
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
}
}

unreachable; // Bad @dependencies source
}

/// In a build.zig file, this function is to `@import` what `lazyDependency` is to `dependency`.
/// If the dependency is lazy and has not yet been fetched, it instructs the parent process to fetch
/// that dependency after the build script has finished running, then returns `null`.
/// If the dependency is lazy but has already been fetched, or if it is eager, it returns
/// the build.zig struct of that dependency, just like a regular `@import`.
pub inline fn lazyImport(
b: *Build,
/// The build.zig struct of the package importing the dependency.
/// When calling this function from the `build` function of a build.zig file's, you normally
/// pass `@This()`.
comptime asking_build_zig: type,
comptime dep_name: []const u8,
) ?type {
const build_runner = @import("root");
const deps = build_runner.dependencies;
const pkg_hash = findImportPkgHashOrFatal(b, asking_build_zig, dep_name);

inline for (@typeInfo(deps.packages).Struct.decls) |decl| {
if (comptime mem.eql(u8, decl.name, pkg_hash)) {
const pkg = @field(deps.packages, decl.name);
const available = !@hasDecl(pkg, "available") or pkg.available;
if (!available) {
markNeededLazyDep(b, pkg_hash);
return null;
}
return if (@hasDecl(pkg, "build_zig"))
pkg.build_zig
else
@compileError("dependency '" ++ dep_name ++ "' does not have a build.zig");
}
}

comptime unreachable; // Bad @dependencies source
}

pub fn anonymousDependency(
b: *Build,
/// The path to the directory containing the dependency's build.zig file,
Expand All @@ -1901,7 +1962,7 @@ pub fn anonymousDependency(
'/', '\\' => byte.* = '.',
else => continue,
};
return dependencyInner(b, name, build_root, build_zig, &.{}, args);
return dependencyInner(b, name, build_root, build_zig, "anonymous", &.{}, args);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can ignore "anonymous" here, it's just a dummy value that makes it so that package hash comparisons always fail.
Removing anonymousDependency is an accepted proposal and I have a different PR up that removes it (#18590).

}

fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
Expand Down Expand Up @@ -1956,6 +2017,7 @@ pub fn dependencyInner(
name: []const u8,
build_root_string: []const u8,
comptime build_zig: ?type,
pkg_hash: []const u8,
pkg_deps: AvailableDeps,
args: anytype,
) *Dependency {
Expand All @@ -1976,7 +2038,7 @@ pub fn dependencyInner(
},
};

const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error");
const sub_builder = b.createChild(name, build_root, pkg_hash, pkg_deps, user_input_options) catch @panic("unhandled error");
if (build_zig) |bz| {
sub_builder.runBuild(bz) catch @panic("unhandled error");

Expand Down
2 changes: 1 addition & 1 deletion lib/std/math/nextafter.zig
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ test "int" {
}

test "float" {
@setEvalBranchQuota(2000);
@setEvalBranchQuota(3000);

// normal -> normal
try expect(nextAfter(f16, 0x1.234p0, 2.0) == 0x1.238p0);
Expand Down
6 changes: 3 additions & 3 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7525,10 +7525,12 @@ fn analyzeCall(

var is_generic_call = func_ty_info.is_generic;
var is_comptime_call = block.is_comptime or modifier == .compile_time;
var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline;
var comptime_reason: ?*const Block.ComptimeReason = null;
if (!is_comptime_call) {
if (!is_inline_call and !is_comptime_call) {
if (sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) |ct| {
is_comptime_call = ct;
is_inline_call = ct;
if (ct) {
comptime_reason = &.{ .comptime_ret_ty = .{
.block = block,
Expand All @@ -7542,8 +7544,6 @@ fn analyzeCall(
else => |e| return e,
}
}
var is_inline_call = is_comptime_call or modifier == .always_inline or
func_ty_info.cc == .Inline;

if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
const msg = msg: {
Expand Down
14 changes: 14 additions & 0 deletions test/behavior/fn.zig
Original file line number Diff line number Diff line change
Expand Up @@ -604,3 +604,17 @@ test "comptime parameters don't have to be marked comptime if only called at com
};
comptime std.debug.assert(S.foo(5, 6) == 11);
}

test "inline function with comptime-known comptime-only return type called at runtime" {
const S = struct {
inline fn foo(x: *i32, y: *const i32) type {
x.* = y.*;
return f32;
}
};
var a: i32 = 0;
const b: i32 = 111;
const T = S.foo(&a, &b);
try expectEqual(111, a);
try expectEqual(f32, T);
}